223 lines
6.2 KiB
Go
223 lines
6.2 KiB
Go
|
|
package metastore
|
||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"warpbox/lib/config"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestOpenClose(t *testing.T) {
|
||
|
|
store, err := Open(t.TempDir())
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("Open returned error: %v", err)
|
||
|
|
}
|
||
|
|
if err := store.Close(); err != nil {
|
||
|
|
t.Fatalf("Close returned error: %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestBootstrapAdminFromPassword(t *testing.T) {
|
||
|
|
clearMetastoreConfigEnv(t)
|
||
|
|
t.Setenv("WARPBOX_ADMIN_PASSWORD", "secret-pass")
|
||
|
|
t.Setenv("WARPBOX_ADMIN_EMAIL", "admin@example.test")
|
||
|
|
|
||
|
|
cfg, err := config.Load()
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("Load returned error: %v", err)
|
||
|
|
}
|
||
|
|
store := openTestStore(t)
|
||
|
|
|
||
|
|
result, err := BootstrapAdmin(cfg, store)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("BootstrapAdmin returned error: %v", err)
|
||
|
|
}
|
||
|
|
if !result.AdminLoginEnabled {
|
||
|
|
t.Fatal("expected admin login to be enabled")
|
||
|
|
}
|
||
|
|
if !result.AdminTag.Protected {
|
||
|
|
t.Fatal("expected admin tag to be protected")
|
||
|
|
}
|
||
|
|
if result.AdminUser == nil {
|
||
|
|
t.Fatal("expected bootstrap admin user")
|
||
|
|
}
|
||
|
|
if !hasString(result.AdminUser.TagIDs, result.AdminTag.ID) {
|
||
|
|
t.Fatal("expected bootstrap admin to have admin tag")
|
||
|
|
}
|
||
|
|
if !VerifyPassword(result.AdminUser.PasswordHash, "secret-pass") {
|
||
|
|
t.Fatal("expected bootstrap admin password to verify")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestBootstrapAdminDisabledWithoutPassword(t *testing.T) {
|
||
|
|
clearMetastoreConfigEnv(t)
|
||
|
|
|
||
|
|
cfg, err := config.Load()
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("Load returned error: %v", err)
|
||
|
|
}
|
||
|
|
store := openTestStore(t)
|
||
|
|
|
||
|
|
result, err := BootstrapAdmin(cfg, store)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("BootstrapAdmin returned error: %v", err)
|
||
|
|
}
|
||
|
|
if result.AdminLoginEnabled {
|
||
|
|
t.Fatal("expected admin login to be disabled without password or existing admin")
|
||
|
|
}
|
||
|
|
if !result.AdminTag.Protected {
|
||
|
|
t.Fatal("expected admin tag to still be created")
|
||
|
|
}
|
||
|
|
users, err := store.ListUsers()
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("ListUsers returned error: %v", err)
|
||
|
|
}
|
||
|
|
if len(users) != 0 {
|
||
|
|
t.Fatalf("expected no users, got %d", len(users))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestDuplicateUsersAndTags(t *testing.T) {
|
||
|
|
store := openTestStore(t)
|
||
|
|
|
||
|
|
if _, err := store.CreateUserWithPassword("alex", "alex@example.test", "secret", nil); err != nil {
|
||
|
|
t.Fatalf("CreateUserWithPassword returned error: %v", err)
|
||
|
|
}
|
||
|
|
if _, err := store.CreateUserWithPassword("Alex", "other@example.test", "secret", nil); !errors.Is(err, ErrDuplicate) {
|
||
|
|
t.Fatalf("expected duplicate username error, got %v", err)
|
||
|
|
}
|
||
|
|
if _, err := store.CreateUserWithPassword("other", "alex@example.test", "secret", nil); !errors.Is(err, ErrDuplicate) {
|
||
|
|
t.Fatalf("expected duplicate email error, got %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
tag := Tag{Name: "staff"}
|
||
|
|
if err := store.CreateTag(&tag); err != nil {
|
||
|
|
t.Fatalf("CreateTag returned error: %v", err)
|
||
|
|
}
|
||
|
|
duplicate := Tag{Name: "Staff"}
|
||
|
|
if err := store.CreateTag(&duplicate); !errors.Is(err, ErrDuplicate) {
|
||
|
|
t.Fatalf("expected duplicate tag error, got %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestPermissionResolutionAndGlobalCaps(t *testing.T) {
|
||
|
|
clearMetastoreConfigEnv(t)
|
||
|
|
t.Setenv("WARPBOX_DEFAULT_USER_MAX_FILE_SIZE_BYTES", "50")
|
||
|
|
t.Setenv("WARPBOX_GLOBAL_MAX_FILE_SIZE_BYTES", "100")
|
||
|
|
t.Setenv("WARPBOX_GLOBAL_MAX_BOX_SIZE_BYTES", "1000")
|
||
|
|
|
||
|
|
cfg, err := config.Load()
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("Load returned error: %v", err)
|
||
|
|
}
|
||
|
|
tagFileLimit := int64(80)
|
||
|
|
tagBoxLimit := int64(2000)
|
||
|
|
userFileLimit := int64(60)
|
||
|
|
user := User{MaxFileSizeBytes: &userFileLimit}
|
||
|
|
tags := []Tag{
|
||
|
|
{
|
||
|
|
Permissions: TagPermissions{
|
||
|
|
UploadAllowed: true,
|
||
|
|
AllowedExpirySeconds: []int64{3600, 600},
|
||
|
|
MaxFileSizeBytes: &tagFileLimit,
|
||
|
|
MaxBoxSizeBytes: &tagBoxLimit,
|
||
|
|
ZipDownloadAllowed: true,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
perms := ResolveUserPermissions(cfg, user, tags)
|
||
|
|
if !perms.UploadAllowed || !perms.ZipDownloadAllowed {
|
||
|
|
t.Fatal("expected tag booleans to grant permissions")
|
||
|
|
}
|
||
|
|
if perms.MaxFileSizeBytes != 80 {
|
||
|
|
t.Fatalf("expected tag limit to beat user/default limit, got %d", perms.MaxFileSizeBytes)
|
||
|
|
}
|
||
|
|
if perms.MaxBoxSizeBytes != 1000 {
|
||
|
|
t.Fatalf("expected global max box cap, got %d", perms.MaxBoxSizeBytes)
|
||
|
|
}
|
||
|
|
if len(perms.AllowedExpirySeconds) != 2 || perms.AllowedExpirySeconds[0] != 600 || perms.AllowedExpirySeconds[1] != 3600 {
|
||
|
|
t.Fatalf("unexpected expiry durations: %#v", perms.AllowedExpirySeconds)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestSettingsStorageAndPrecedence(t *testing.T) {
|
||
|
|
clearMetastoreConfigEnv(t)
|
||
|
|
t.Setenv("WARPBOX_API_ENABLED", "true")
|
||
|
|
|
||
|
|
store := openTestStore(t)
|
||
|
|
if err := store.SetSetting(config.SettingAPIEnabled, "false"); err != nil {
|
||
|
|
t.Fatalf("SetSetting returned error: %v", err)
|
||
|
|
}
|
||
|
|
overrides, err := store.ListSettings()
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("ListSettings returned error: %v", err)
|
||
|
|
}
|
||
|
|
cfg, err := config.Load()
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("Load returned error: %v", err)
|
||
|
|
}
|
||
|
|
if err := cfg.ApplyOverrides(overrides); err != nil {
|
||
|
|
t.Fatalf("ApplyOverrides returned error: %v", err)
|
||
|
|
}
|
||
|
|
if cfg.APIEnabled {
|
||
|
|
t.Fatal("expected stored DB override to beat env")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestSessionExpiry(t *testing.T) {
|
||
|
|
store := openTestStore(t)
|
||
|
|
session, err := store.CreateSession("user-id", time.Millisecond)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("CreateSession returned error: %v", err)
|
||
|
|
}
|
||
|
|
time.Sleep(2 * time.Millisecond)
|
||
|
|
if _, ok, err := store.GetSession(session.Token); err != nil || ok {
|
||
|
|
t.Fatalf("expected expired session to be invalid, ok=%v err=%v", ok, err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func openTestStore(t *testing.T) *Store {
|
||
|
|
t.Helper()
|
||
|
|
store, err := Open(t.TempDir())
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("Open returned error: %v", err)
|
||
|
|
}
|
||
|
|
t.Cleanup(func() {
|
||
|
|
_ = store.Close()
|
||
|
|
})
|
||
|
|
return store
|
||
|
|
}
|
||
|
|
|
||
|
|
func clearMetastoreConfigEnv(t *testing.T) {
|
||
|
|
t.Helper()
|
||
|
|
for _, name := range []string{
|
||
|
|
"WARPBOX_DATA_DIR",
|
||
|
|
"WARPBOX_ADMIN_PASSWORD",
|
||
|
|
"WARPBOX_ADMIN_USERNAME",
|
||
|
|
"WARPBOX_ADMIN_EMAIL",
|
||
|
|
"WARPBOX_ADMIN_ENABLED",
|
||
|
|
"WARPBOX_ALLOW_ADMIN_SETTINGS_OVERRIDE",
|
||
|
|
"WARPBOX_ADMIN_COOKIE_SECURE",
|
||
|
|
"WARPBOX_GUEST_UPLOADS_ENABLED",
|
||
|
|
"WARPBOX_API_ENABLED",
|
||
|
|
"WARPBOX_ZIP_DOWNLOADS_ENABLED",
|
||
|
|
"WARPBOX_ONE_TIME_DOWNLOADS_ENABLED",
|
||
|
|
"WARPBOX_RENEW_ON_ACCESS_ENABLED",
|
||
|
|
"WARPBOX_RENEW_ON_DOWNLOAD_ENABLED",
|
||
|
|
"WARPBOX_DEFAULT_GUEST_EXPIRY_SECONDS",
|
||
|
|
"WARPBOX_MAX_GUEST_EXPIRY_SECONDS",
|
||
|
|
"WARPBOX_GLOBAL_MAX_FILE_SIZE_BYTES",
|
||
|
|
"WARPBOX_GLOBAL_MAX_BOX_SIZE_BYTES",
|
||
|
|
"WARPBOX_DEFAULT_USER_MAX_FILE_SIZE_BYTES",
|
||
|
|
"WARPBOX_DEFAULT_USER_MAX_BOX_SIZE_BYTES",
|
||
|
|
"WARPBOX_SESSION_TTL_SECONDS",
|
||
|
|
"WARPBOX_BOX_POLL_INTERVAL_MS",
|
||
|
|
"WARPBOX_THUMBNAIL_BATCH_SIZE",
|
||
|
|
"WARPBOX_THUMBNAIL_INTERVAL_SECONDS",
|
||
|
|
} {
|
||
|
|
t.Setenv(name, "")
|
||
|
|
}
|
||
|
|
}
|