Files
warpbox/lib/server/account_box_manager_test.go
Daniel Legt 89c885f637 feat(models): add box activity tracking
Adds BoxActivity model to track actions taken on a box.
Updates related endpoints and UI for activity feed.
2026-04-30 19:55:32 +03:00

220 lines
8.2 KiB
Go

package server
import (
"net/http"
"net/http/httptest"
"net/url"
"os"
"strings"
"testing"
"warpbox/lib/boxstore"
"warpbox/lib/metastore"
)
func TestAccountBoxManagerAdminCanViewAndEdit(t *testing.T) {
app, admin := setupAccountTestApp(t)
router := setupAccountTestRouter(t, app)
session := createAccountTestSession(t, app, admin)
id := "abababababababababababababababab"
createIndexedBox(t, app, id, "", "", 10, false)
response := getAccountBoxManager(router, session, id)
if response.Code != http.StatusOK {
t.Fatalf("expected manager page, got %d body=%s", response.Code, response.Body.String())
}
if !strings.Contains(response.Body.String(), "WarpBox Box Manager") {
t.Fatal("expected manager UI")
}
form := url.Values{"disable_zip": []string{"true"}}
response = postAccountBoxForm(router, session, "/account/boxes/"+id, form)
if response.Code != http.StatusSeeOther {
t.Fatalf("expected update redirect, got %d", response.Code)
}
manifest, err := boxstore.ReadManifest(id)
if err != nil {
t.Fatalf("ReadManifest returned error: %v", err)
}
if !manifest.DisableZip {
t.Fatal("expected sharing rule update")
}
}
func TestAccountBoxManagerOwnerViewAllowedAndDeniedByPolicy(t *testing.T) {
app, _ := setupAccountTestApp(t)
user, err := app.store.CreateUserWithPassword("owner-view", "owner-view@example.test", "secret", nil)
if err != nil {
t.Fatalf("CreateUserWithPassword returned error: %v", err)
}
router := setupAccountTestRouter(t, app)
session := createAccountTestSession(t, app, user)
id := "bcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbc"
createIndexedBox(t, app, id, user.ID, user.Username, 10, false)
response := getAccountBoxManager(router, session, id)
if response.Code != http.StatusOK {
t.Fatalf("expected owner manager page, got %d", response.Code)
}
app.config.BoxOwnerEditEnabled = false
response = getAccountBoxManager(router, session, id)
if response.Code != http.StatusForbidden {
t.Fatalf("expected owner denied by policy, got %d", response.Code)
}
}
func TestAccountBoxManagerOwnerRefreshLimits(t *testing.T) {
app, _ := setupAccountTestApp(t)
app.config.BoxOwnerMaxRefreshCount = 1
app.config.BoxOwnerMaxRefreshAmountSeconds = 60
app.config.BoxOwnerMaxTotalExpirySeconds = 7200
user, err := app.store.CreateUserWithPassword("owner-refresh", "owner-refresh@example.test", "secret", nil)
if err != nil {
t.Fatalf("CreateUserWithPassword returned error: %v", err)
}
router := setupAccountTestRouter(t, app)
session := createAccountTestSession(t, app, user)
id := "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"
createIndexedBox(t, app, id, user.ID, user.Username, 10, false)
response := postAccountBoxForm(router, session, "/account/boxes/"+id+"/extend", url.Values{"extend_seconds": []string{"60"}})
if response.Code != http.StatusSeeOther {
t.Fatalf("expected owner refresh success, got %d body=%s", response.Code, response.Body.String())
}
record, ok, err := app.store.GetBoxRecord(id)
if err != nil || !ok {
t.Fatalf("GetBoxRecord returned ok=%v err=%v", ok, err)
}
if record.RefreshCount != 1 {
t.Fatalf("expected refresh count 1, got %d", record.RefreshCount)
}
response = postAccountBoxForm(router, session, "/account/boxes/"+id+"/extend", url.Values{"extend_seconds": []string{"60"}})
if response.Code != http.StatusOK {
t.Fatalf("expected refresh count rejection render, got %d", response.Code)
}
if !strings.Contains(response.Body.String(), "refresh count") {
t.Fatal("expected refresh count error")
}
}
func TestAccountBoxManagerOwnerRefreshRejectedOverMaxDuration(t *testing.T) {
app, _ := setupAccountTestApp(t)
app.config.BoxOwnerMaxRefreshAmountSeconds = 60
user, err := app.store.CreateUserWithPassword("owner-duration", "owner-duration@example.test", "secret", nil)
if err != nil {
t.Fatalf("CreateUserWithPassword returned error: %v", err)
}
router := setupAccountTestRouter(t, app)
session := createAccountTestSession(t, app, user)
id := "dededededededededededededededede"
createIndexedBox(t, app, id, user.ID, user.Username, 10, false)
response := postAccountBoxForm(router, session, "/account/boxes/"+id+"/extend", url.Values{"extend_seconds": []string{"120"}})
if response.Code != http.StatusOK {
t.Fatalf("expected max duration rejection render, got %d", response.Code)
}
if !strings.Contains(response.Body.String(), "maximum single extension") {
t.Fatal("expected max duration error")
}
}
func TestAccountBoxManagerPasswordSetRemovePermissions(t *testing.T) {
app, _ := setupAccountTestApp(t)
user, err := app.store.CreateUserWithPassword("owner-pass", "owner-pass@example.test", "secret", nil)
if err != nil {
t.Fatalf("CreateUserWithPassword returned error: %v", err)
}
router := setupAccountTestRouter(t, app)
session := createAccountTestSession(t, app, user)
id := "efefefefefefefefefefefefefefefef"
createIndexedBox(t, app, id, user.ID, user.Username, 10, false)
response := postAccountBoxForm(router, session, "/account/boxes/"+id+"/password", url.Values{"password": []string{"new-secret"}})
if response.Code != http.StatusSeeOther {
t.Fatalf("expected password set redirect, got %d", response.Code)
}
manifest, err := boxstore.ReadManifest(id)
if err != nil {
t.Fatalf("ReadManifest returned error: %v", err)
}
if manifest.PasswordHash == "" || manifest.AuthToken == "" {
t.Fatal("expected password set")
}
app.config.BoxOwnerPasswordEditEnabled = false
response = postAccountBoxForm(router, session, "/account/boxes/"+id+"/password/remove", nil)
if response.Code != http.StatusOK {
t.Fatalf("expected password permission render, got %d", response.Code)
}
if !strings.Contains(response.Body.String(), "password editing disabled") {
t.Fatal("expected password permission error")
}
}
func TestAccountBoxManagerFileDeleteAndBoxDeletePermissions(t *testing.T) {
app, _ := setupAccountTestApp(t)
user, err := app.store.CreateUserWithPassword("owner-delete", "owner-delete@example.test", "secret", nil)
if err != nil {
t.Fatalf("CreateUserWithPassword returned error: %v", err)
}
router := setupAccountTestRouter(t, app)
session := createAccountTestSession(t, app, user)
id := "fafafafafafafafafafafafafafafafa"
createIndexedBox(t, app, id, user.ID, user.Username, 10, false)
manifest, err := boxstore.ReadManifest(id)
if err != nil {
t.Fatalf("ReadManifest returned error: %v", err)
}
fileID := manifest.Files[0].ID
response := postAccountBoxForm(router, session, "/account/boxes/"+id+"/files/delete", url.Values{"file_ids": []string{fileID}})
if response.Code != http.StatusSeeOther {
t.Fatalf("expected file delete redirect, got %d", response.Code)
}
manifest, err = boxstore.ReadManifest(id)
if err != nil {
t.Fatalf("ReadManifest returned error: %v", err)
}
if len(manifest.Files) != 0 {
t.Fatalf("expected file removed, got %#v", manifest.Files)
}
app.config.BoxOwnerEditEnabled = false
response = postAccountBoxForm(router, session, "/account/boxes/"+id+"/delete", nil)
if response.Code != http.StatusForbidden {
t.Fatalf("expected delete permission denied after policy disabled, got %d", response.Code)
}
app.config.BoxOwnerEditEnabled = true
response = postAccountBoxForm(router, session, "/account/boxes/"+id+"/delete", nil)
if response.Code != http.StatusSeeOther {
t.Fatalf("expected box delete redirect, got %d", response.Code)
}
if _, err := os.Stat(boxstore.BoxPath(id)); !os.IsNotExist(err) {
t.Fatalf("expected box directory deleted, stat err=%v", err)
}
}
func getAccountBoxManager(router http.Handler, session metastore.Session, id string) *httptest.ResponseRecorder {
request := httptest.NewRequest(http.MethodGet, "/account/boxes/"+id, nil)
request.AddCookie(&http.Cookie{Name: accountSessionCookie, Value: session.Token})
response := httptest.NewRecorder()
router.ServeHTTP(response, request)
return response
}
func postAccountBoxForm(router http.Handler, session metastore.Session, path string, values url.Values) *httptest.ResponseRecorder {
if values == nil {
values = url.Values{}
}
values.Set("csrf_token", session.CSRFToken)
request := httptest.NewRequest(http.MethodPost, path, strings.NewReader(values.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
}