package server import ( "encoding/json" "net/http" "net/http/httptest" "net/url" "strings" "testing" "time" "warpbox/lib/config" "warpbox/lib/metastore" ) func TestAccountSettingsPermissionDenied(t *testing.T) { app, _ := setupAccountTestApp(t) user, err := app.store.CreateUserWithPassword("regular", "regular@example.test", "secret", nil) if err != nil { t.Fatalf("CreateUserWithPassword returned error: %v", err) } router := setupAccountTestRouter(t, app) session := createAccountTestSession(t, app, user) request := httptest.NewRequest(http.MethodGet, "/account/settings", nil) request.AddCookie(&http.Cookie{Name: accountSessionCookie, Value: session.Token}) response := httptest.NewRecorder() router.ServeHTTP(response, request) if response.Code != http.StatusForbidden { t.Fatalf("expected permission denied, got %d", response.Code) } } func TestAccountSettingsPageLoadsForAdmin(t *testing.T) { app, user := setupAccountTestApp(t) router := setupAccountTestRouter(t, app) session := createAccountTestSession(t, app, user) request := httptest.NewRequest(http.MethodGet, "/account/settings", nil) request.AddCookie(&http.Cookie{Name: accountSessionCookie, Value: session.Token}) response := httptest.NewRecorder() router.ServeHTTP(response, request) if response.Code != http.StatusOK { t.Fatalf("expected settings page, got %d body=%s", response.Code, response.Body.String()) } for _, text := range []string{"Uploads", "Downloads", "Box policy", "Save Settings"} { if !strings.Contains(response.Body.String(), text) { t.Fatalf("expected settings page to contain %q", text) } } } func TestAccountSettingsValidUpdate(t *testing.T) { app, user := setupAccountTestApp(t) router := setupAccountTestRouter(t, app) session := createAccountTestSession(t, app, user) form := url.Values{} form.Set("csrf_token", session.CSRFToken) form.Set(config.SettingAPIEnabled, "false") response := postAccountSettingsForm(router, session, form) if response.Code != http.StatusSeeOther { t.Fatalf("expected settings redirect, got %d body=%s", response.Code, response.Body.String()) } if app.config.APIEnabled { t.Fatal("expected API setting to be disabled") } value, ok, err := app.store.GetSetting(config.SettingAPIEnabled) if err != nil || !ok || value != "false" { t.Fatalf("expected API setting override false, got value=%q ok=%v err=%v", value, ok, err) } } func TestAccountSettingsInvalidUpdate(t *testing.T) { app, user := setupAccountTestApp(t) router := setupAccountTestRouter(t, app) session := createAccountTestSession(t, app, user) form := url.Values{} form.Set("csrf_token", session.CSRFToken) form.Set(config.SettingSessionTTLSeconds, "1") response := postAccountSettingsForm(router, session, form) if response.Code != http.StatusOK { t.Fatalf("expected settings form render, got %d", response.Code) } if !strings.Contains(response.Body.String(), "must be at least 60") { t.Fatal("expected validation error in response") } } func TestAccountSettingsLockedSettingCannotChange(t *testing.T) { app, user := setupAccountTestApp(t) router := setupAccountTestRouter(t, app) session := createAccountTestSession(t, app, user) form := url.Values{} form.Set("csrf_token", session.CSRFToken) form.Set(config.SettingGlobalMaxFileSizeBytes, "1") response := postAccountSettingsForm(router, session, form) if response.Code != http.StatusOK { t.Fatalf("expected settings form render, got %d", response.Code) } if !strings.Contains(response.Body.String(), "locked") { t.Fatal("expected locked setting error") } if value, ok, err := app.store.GetSetting(config.SettingGlobalMaxFileSizeBytes); err != nil || ok || value != "" { t.Fatalf("expected no locked setting override, got value=%q ok=%v err=%v", value, ok, err) } } func TestAccountSettingsImportRejectsUnknownOrInvalidSettings(t *testing.T) { app, user := setupAccountTestApp(t) router := setupAccountTestRouter(t, app) session := createAccountTestSession(t, app, user) for _, body := range []string{ `{"version":1,"settings":{"not_real":"true"}}`, `{"version":1,"settings":{"session_ttl_seconds":"1"}}`, } { response := postAccountSettingsJSON(router, session, body) if response.Code != http.StatusBadRequest { t.Fatalf("expected bad import for %s, got %d", body, response.Code) } } } func TestAccountSettingsImportAppliesValidSettings(t *testing.T) { app, user := setupAccountTestApp(t) router := setupAccountTestRouter(t, app) session := createAccountTestSession(t, app, user) response := postAccountSettingsJSON(router, session, `{"version":1,"settings":{"api_enabled":"false","box_owner_max_refresh_count":"7"}}`) if response.Code != http.StatusOK { t.Fatalf("expected import success, got %d body=%s", response.Code, response.Body.String()) } if app.config.APIEnabled { t.Fatal("expected imported API setting to be disabled") } if app.config.BoxOwnerMaxRefreshCount != 7 { t.Fatalf("expected imported box owner refresh count 7, got %d", app.config.BoxOwnerMaxRefreshCount) } } func TestAccountSettingsExportShape(t *testing.T) { app, user := setupAccountTestApp(t) router := setupAccountTestRouter(t, app) session := createAccountTestSession(t, app, user) request := httptest.NewRequest(http.MethodGet, "/account/settings/export.json", nil) request.AddCookie(&http.Cookie{Name: accountSessionCookie, Value: session.Token}) response := httptest.NewRecorder() router.ServeHTTP(response, request) if response.Code != http.StatusOK { t.Fatalf("expected export success, got %d", response.Code) } var backup SettingsBackup if err := json.Unmarshal(response.Body.Bytes(), &backup); err != nil { t.Fatalf("Unmarshal returned error: %v", err) } if backup.Version != 1 { t.Fatalf("expected version 1, got %d", backup.Version) } if _, ok := backup.Settings[config.SettingBoxOwnerMaxRefreshCount]; !ok { t.Fatal("expected export to include box owner policy setting") } if _, ok := backup.Settings[config.SettingDataDir]; ok { t.Fatal("did not expect locked data dir in export settings") } } func createAccountTestSession(t *testing.T, app *App, user metastore.User) metastore.Session { t.Helper() session, err := app.store.CreateSession(user.ID, time.Hour) if err != nil { t.Fatalf("CreateSession returned error: %v", err) } return session } func postAccountSettingsForm(router http.Handler, session metastore.Session, form url.Values) *httptest.ResponseRecorder { request := httptest.NewRequest(http.MethodPost, "/account/settings", strings.NewReader(form.Encode())) request.Header.Set("Content-Type", "application/x-www-form-urlencoded") request.AddCookie(&http.Cookie{Name: accountSessionCookie, Value: session.Token}) response := httptest.NewRecorder() router.ServeHTTP(response, request) return response } func postAccountSettingsJSON(router http.Handler, session metastore.Session, body string) *httptest.ResponseRecorder { request := httptest.NewRequest(http.MethodPost, "/account/settings/import.json", strings.NewReader(body)) request.Header.Set("Content-Type", "application/json") request.Header.Set("X-CSRF-Token", session.CSRFToken) request.AddCookie(&http.Cookie{Name: accountSessionCookie, Value: session.Token}) response := httptest.NewRecorder() router.ServeHTTP(response, request) return response }