221 lines
8.2 KiB
Go
221 lines
8.2 KiB
Go
|
|
package server
|
||
|
|
|
||
|
|
import (
|
||
|
|
"net/http"
|
||
|
|
"net/http/httptest"
|
||
|
|
"net/url"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"warpbox/lib/boxstore"
|
||
|
|
"warpbox/lib/metastore"
|
||
|
|
"warpbox/lib/models"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestAccountBoxesAdminListsBoxes(t *testing.T) {
|
||
|
|
app, user := setupAccountTestApp(t)
|
||
|
|
router := setupAccountTestRouter(t, app)
|
||
|
|
session := createAccountTestSession(t, app, user)
|
||
|
|
createIndexedBox(t, app, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "", "", 10, false)
|
||
|
|
|
||
|
|
response := getAccountBoxes(router, session, "/account/boxes")
|
||
|
|
if response.Code != http.StatusOK {
|
||
|
|
t.Fatalf("expected boxes page, got %d body=%s", response.Code, response.Body.String())
|
||
|
|
}
|
||
|
|
if !strings.Contains(response.Body.String(), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") {
|
||
|
|
t.Fatal("expected indexed box in admin list")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAccountBoxesRegularUserSeesOnlyOwnBoxes(t *testing.T) {
|
||
|
|
app, _ := setupAccountTestApp(t)
|
||
|
|
user, err := app.store.CreateUserWithPassword("box-user", "box-user@example.test", "secret", nil)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("CreateUserWithPassword returned error: %v", err)
|
||
|
|
}
|
||
|
|
router := setupAccountTestRouter(t, app)
|
||
|
|
session := createAccountTestSession(t, app, user)
|
||
|
|
createIndexedBox(t, app, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", user.ID, user.Username, 10, false)
|
||
|
|
createIndexedBox(t, app, "cccccccccccccccccccccccccccccccc", "other", "other", 20, false)
|
||
|
|
|
||
|
|
response := getAccountBoxes(router, session, "/account/boxes")
|
||
|
|
if response.Code != http.StatusOK {
|
||
|
|
t.Fatalf("expected boxes page, got %d", response.Code)
|
||
|
|
}
|
||
|
|
body := response.Body.String()
|
||
|
|
if !strings.Contains(body, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") {
|
||
|
|
t.Fatal("expected own box")
|
||
|
|
}
|
||
|
|
if strings.Contains(body, "cccccccccccccccccccccccccccccccc") {
|
||
|
|
t.Fatal("did not expect other user's box")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAccountBoxesFiltersSortAndPagination(t *testing.T) {
|
||
|
|
app, user := setupAccountTestApp(t)
|
||
|
|
router := setupAccountTestRouter(t, app)
|
||
|
|
session := createAccountTestSession(t, app, user)
|
||
|
|
createIndexedBox(t, app, "11111111111111111111111111111111", "", "", 10, false)
|
||
|
|
createIndexedBox(t, app, "22222222222222222222222222222222", "", "", 1000, true)
|
||
|
|
createIndexedBox(t, app, "33333333333333333333333333333333", "", "", 500, false)
|
||
|
|
|
||
|
|
response := getAccountBoxes(router, session, "/account/boxes?flag=password&sort=largest&page_size=25")
|
||
|
|
if response.Code != http.StatusOK {
|
||
|
|
t.Fatalf("expected boxes page, got %d", response.Code)
|
||
|
|
}
|
||
|
|
body := response.Body.String()
|
||
|
|
if !strings.Contains(body, "22222222222222222222222222222222") {
|
||
|
|
t.Fatal("expected password filtered box")
|
||
|
|
}
|
||
|
|
if strings.Contains(body, "11111111111111111111111111111111") {
|
||
|
|
t.Fatal("did not expect unfiltered box")
|
||
|
|
}
|
||
|
|
|
||
|
|
page, err := app.store.ListBoxRecords(metastore.BoxFilters{Sort: "largest"}, metastore.BoxPageRequest{Page: 1, PageSize: 25})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("ListBoxRecords returned error: %v", err)
|
||
|
|
}
|
||
|
|
if len(page.Rows) != 3 || page.Rows[0].ID != "22222222222222222222222222222222" {
|
||
|
|
t.Fatalf("expected largest sort first, got %#v", page.Rows)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAccountBoxesBulkExpireAndDelete(t *testing.T) {
|
||
|
|
app, user := setupAccountTestApp(t)
|
||
|
|
router := setupAccountTestRouter(t, app)
|
||
|
|
session := createAccountTestSession(t, app, user)
|
||
|
|
id := "dddddddddddddddddddddddddddddddd"
|
||
|
|
createIndexedBox(t, app, id, "", "", 10, false)
|
||
|
|
|
||
|
|
values := url.Values{"box_ids": []string{id}}
|
||
|
|
response := postAccountBoxesForm(router, session, "/account/boxes/bulk/expire", values)
|
||
|
|
if response.Code != http.StatusSeeOther {
|
||
|
|
t.Fatalf("expected expire redirect, got %d", response.Code)
|
||
|
|
}
|
||
|
|
record, ok, err := app.store.GetBoxRecord(id)
|
||
|
|
if err != nil || !ok {
|
||
|
|
t.Fatalf("GetBoxRecord returned ok=%v err=%v", ok, err)
|
||
|
|
}
|
||
|
|
if record.ExpiresAt.After(time.Now().UTC()) {
|
||
|
|
t.Fatal("expected box to be expired")
|
||
|
|
}
|
||
|
|
|
||
|
|
response = postAccountBoxesForm(router, session, "/account/boxes/bulk/delete", values)
|
||
|
|
if response.Code != http.StatusSeeOther {
|
||
|
|
t.Fatalf("expected delete redirect, got %d", response.Code)
|
||
|
|
}
|
||
|
|
if _, ok, err := app.store.GetBoxRecord(id); err != nil || ok {
|
||
|
|
t.Fatalf("expected deleted record, ok=%v err=%v", ok, err)
|
||
|
|
}
|
||
|
|
if _, err := os.Stat(boxstore.BoxPath(id)); !os.IsNotExist(err) {
|
||
|
|
t.Fatalf("expected box directory deleted, stat err=%v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAccountBoxesBulkDeletePermissionDenied(t *testing.T) {
|
||
|
|
app, _ := setupAccountTestApp(t)
|
||
|
|
user, err := app.store.CreateUserWithPassword("box-limited", "box-limited@example.test", "secret", nil)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("CreateUserWithPassword returned error: %v", err)
|
||
|
|
}
|
||
|
|
router := setupAccountTestRouter(t, app)
|
||
|
|
session := createAccountTestSession(t, app, user)
|
||
|
|
id := "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
|
||
|
|
createIndexedBox(t, app, id, "other", "other", 10, false)
|
||
|
|
|
||
|
|
response := postAccountBoxesForm(router, session, "/account/boxes/bulk/delete", url.Values{"box_ids": []string{id}})
|
||
|
|
if response.Code != http.StatusForbidden {
|
||
|
|
t.Fatalf("expected permission denied, got %d", response.Code)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAccountBoxesBumpExpiryPolicyRejection(t *testing.T) {
|
||
|
|
app, user := setupAccountTestApp(t)
|
||
|
|
app.config.BoxOwnerRefreshEnabled = false
|
||
|
|
router := setupAccountTestRouter(t, app)
|
||
|
|
session := createAccountTestSession(t, app, user)
|
||
|
|
id := "ffffffffffffffffffffffffffffffff"
|
||
|
|
createIndexedBox(t, app, id, "", "", 10, false)
|
||
|
|
|
||
|
|
response := postAccountBoxesForm(router, session, "/account/boxes/bulk/bump-expiry", url.Values{"box_ids": []string{id}, "bump_seconds": []string{"60"}})
|
||
|
|
if response.Code != http.StatusForbidden {
|
||
|
|
t.Fatalf("expected policy rejection, got %d", response.Code)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAccountBoxesDeleteLargest(t *testing.T) {
|
||
|
|
app, user := setupAccountTestApp(t)
|
||
|
|
router := setupAccountTestRouter(t, app)
|
||
|
|
session := createAccountTestSession(t, app, user)
|
||
|
|
small := "12345123451234512345123451234512"
|
||
|
|
large := "99999999999999999999999999999999"
|
||
|
|
createIndexedBox(t, app, small, "", "", 10, false)
|
||
|
|
createIndexedBox(t, app, large, "", "", 1000, false)
|
||
|
|
|
||
|
|
response := postAccountBoxesForm(router, session, "/account/boxes/delete-largest", nil)
|
||
|
|
if response.Code != http.StatusSeeOther {
|
||
|
|
t.Fatalf("expected delete-largest redirect, got %d", response.Code)
|
||
|
|
}
|
||
|
|
if _, ok, err := app.store.GetBoxRecord(large); err != nil || ok {
|
||
|
|
t.Fatalf("expected largest deleted, ok=%v err=%v", ok, err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func createIndexedBox(t *testing.T, app *App, id string, ownerID string, ownerUsername string, size int64, password bool) {
|
||
|
|
t.Helper()
|
||
|
|
if err := os.MkdirAll(boxstore.BoxPath(id), 0755); err != nil {
|
||
|
|
t.Fatalf("MkdirAll returned error: %v", err)
|
||
|
|
}
|
||
|
|
filename := "file-" + id[:4] + ".txt"
|
||
|
|
if err := os.WriteFile(filepath.Join(boxstore.BoxPath(id), filename), []byte(strings.Repeat("x", int(size))), 0644); err != nil {
|
||
|
|
t.Fatalf("WriteFile returned error: %v", err)
|
||
|
|
}
|
||
|
|
manifest := models.BoxManifest{
|
||
|
|
OwnerID: ownerID,
|
||
|
|
OwnerUsername: ownerUsername,
|
||
|
|
Files: []models.BoxFile{{
|
||
|
|
ID: "abcdabcdabcdabcd",
|
||
|
|
Name: filename,
|
||
|
|
Size: size,
|
||
|
|
Status: models.FileStatusReady,
|
||
|
|
}},
|
||
|
|
CreatedAt: time.Now().UTC().Add(-time.Duration(size) * time.Second),
|
||
|
|
ExpiresAt: time.Now().UTC().Add(time.Hour),
|
||
|
|
RetentionSecs: 3600,
|
||
|
|
}
|
||
|
|
if password {
|
||
|
|
manifest.PasswordHash = "hash"
|
||
|
|
manifest.AuthToken = "token"
|
||
|
|
}
|
||
|
|
if err := boxstore.WriteManifest(id, manifest); err != nil {
|
||
|
|
t.Fatalf("WriteManifest returned error: %v", err)
|
||
|
|
}
|
||
|
|
if err := app.store.UpsertBoxRecord(boxRecordFromManifest(id, manifest)); err != nil {
|
||
|
|
t.Fatalf("UpsertBoxRecord returned error: %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func getAccountBoxes(router http.Handler, session metastore.Session, path string) *httptest.ResponseRecorder {
|
||
|
|
request := httptest.NewRequest(http.MethodGet, path, nil)
|
||
|
|
request.AddCookie(&http.Cookie{Name: accountSessionCookie, Value: session.Token})
|
||
|
|
response := httptest.NewRecorder()
|
||
|
|
router.ServeHTTP(response, request)
|
||
|
|
return response
|
||
|
|
}
|
||
|
|
|
||
|
|
func postAccountBoxesForm(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
|
||
|
|
}
|