- Add backend services to create, list, and delete API tokens. - Implement Bearer token authentication to resolve tokens to users. - Register HTTP routes for managing user tokens under `/account/tokens`. - Add tests to verify that uploads with valid Bearer tokens associate the upload with the correct user, while invalid tokens fall back to anonymous uploads.
600 lines
23 KiB
Go
600 lines
23 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"warpbox.dev/backend/libs/services"
|
|
)
|
|
|
|
func TestLoggedInUploadStoresOwnerAndAnonymousUploadDoesNot(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
|
|
user, err := app.authService.CreateBootstrapUser("daniel", "daniel@example.test", "password123")
|
|
if err != nil {
|
|
t.Fatalf("CreateBootstrapUser returned error: %v", err)
|
|
}
|
|
_, token, err := app.authService.Login("daniel@example.test", "password123")
|
|
if err != nil {
|
|
t.Fatalf("Login returned error: %v", err)
|
|
}
|
|
|
|
request := multipartUploadRequest(t, "/api/v1/upload", "file", "owned.txt", "owned")
|
|
request.Header.Set("Accept", "application/json")
|
|
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
|
|
response := httptest.NewRecorder()
|
|
app.Upload(response, request)
|
|
if response.Code != http.StatusCreated {
|
|
t.Fatalf("owned upload status = %d, body = %s", response.Code, response.Body.String())
|
|
}
|
|
var ownedPayload services.UploadResult
|
|
if err := json.Unmarshal(response.Body.Bytes(), &ownedPayload); err != nil {
|
|
t.Fatalf("json.Unmarshal owned returned error: %v", err)
|
|
}
|
|
ownedBox, err := app.uploadService.GetBox(ownedPayload.BoxID)
|
|
if err != nil {
|
|
t.Fatalf("GetBox owned returned error: %v", err)
|
|
}
|
|
if ownedBox.OwnerID != user.ID {
|
|
t.Fatalf("owned OwnerID = %q, want %q", ownedBox.OwnerID, user.ID)
|
|
}
|
|
|
|
owned := uploadThroughApp(t, app)
|
|
anonymous, err := app.uploadService.GetBox(owned.BoxID)
|
|
if err != nil {
|
|
t.Fatalf("GetBox anonymous returned error: %v", err)
|
|
}
|
|
if anonymous.OwnerID != "" {
|
|
t.Fatalf("anonymous OwnerID = %q, want empty", anonymous.OwnerID)
|
|
}
|
|
|
|
boxes, err := app.uploadService.ListBoxes(0)
|
|
if err != nil {
|
|
t.Fatalf("ListBoxes returned error: %v", err)
|
|
}
|
|
foundOwned := false
|
|
for _, box := range boxes {
|
|
if box.OwnerID == user.ID {
|
|
foundOwned = true
|
|
}
|
|
}
|
|
if !foundOwned {
|
|
t.Fatalf("logged-in upload did not store owner id %q", user.ID)
|
|
}
|
|
}
|
|
|
|
func TestBearerTokenUploadActsAsUser(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
|
|
user, err := app.authService.CreateBootstrapUser("daniel", "daniel@example.test", "password123")
|
|
if err != nil {
|
|
t.Fatalf("CreateBootstrapUser returned error: %v", err)
|
|
}
|
|
tokenResult, err := app.authService.CreateAPIToken(user.ID, "cli")
|
|
if err != nil {
|
|
t.Fatalf("CreateAPIToken returned error: %v", err)
|
|
}
|
|
|
|
request := multipartUploadRequest(t, "/api/v1/upload", "file", "owned.txt", "owned")
|
|
request.Header.Set("Accept", "application/json")
|
|
request.Header.Set("Authorization", "Bearer "+tokenResult.Plaintext)
|
|
response := httptest.NewRecorder()
|
|
app.Upload(response, request)
|
|
if response.Code != http.StatusCreated {
|
|
t.Fatalf("token upload status = %d, body = %s", response.Code, response.Body.String())
|
|
}
|
|
var payload services.UploadResult
|
|
if err := json.Unmarshal(response.Body.Bytes(), &payload); err != nil {
|
|
t.Fatalf("json.Unmarshal returned error: %v", err)
|
|
}
|
|
box, err := app.uploadService.GetBox(payload.BoxID)
|
|
if err != nil {
|
|
t.Fatalf("GetBox returned error: %v", err)
|
|
}
|
|
if box.OwnerID != user.ID {
|
|
t.Fatalf("OwnerID = %q, want %q", box.OwnerID, user.ID)
|
|
}
|
|
|
|
// An invalid bearer token must not authenticate as the user.
|
|
badRequest := multipartUploadRequest(t, "/api/v1/upload", "file", "x.txt", "x")
|
|
badRequest.Header.Set("Accept", "application/json")
|
|
badRequest.Header.Set("Authorization", "Bearer wbx_bogus.secret")
|
|
badResponse := httptest.NewRecorder()
|
|
app.Upload(badResponse, badRequest)
|
|
if badResponse.Code != http.StatusCreated {
|
|
t.Fatalf("anonymous fallback upload status = %d, body = %s", badResponse.Code, badResponse.Body.String())
|
|
}
|
|
var badPayload services.UploadResult
|
|
if err := json.Unmarshal(badResponse.Body.Bytes(), &badPayload); err != nil {
|
|
t.Fatalf("json.Unmarshal returned error: %v", err)
|
|
}
|
|
badBox, err := app.uploadService.GetBox(badPayload.BoxID)
|
|
if err != nil {
|
|
t.Fatalf("GetBox returned error: %v", err)
|
|
}
|
|
if badBox.OwnerID != "" {
|
|
t.Fatalf("invalid token OwnerID = %q, want empty", badBox.OwnerID)
|
|
}
|
|
}
|
|
|
|
func TestInviteHandlerCreatesUserAndMarksInviteUsed(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
|
|
admin, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
|
|
if err != nil {
|
|
t.Fatalf("CreateBootstrapUser returned error: %v", err)
|
|
}
|
|
invite, err := app.authService.CreateInvite("friend@example.test", services.UserRoleUser, admin.ID, 0)
|
|
if err != nil {
|
|
t.Fatalf("CreateInvite returned error: %v", err)
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodPost, "/invite/"+invite.Token, strings.NewReader("username=friend&password=password123"))
|
|
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
request.SetPathValue("token", invite.Token)
|
|
response := httptest.NewRecorder()
|
|
app.InvitePost(response, request)
|
|
if response.Code != http.StatusSeeOther {
|
|
t.Fatalf("InvitePost status = %d, body = %s", response.Code, response.Body.String())
|
|
}
|
|
if _, err := app.authService.AcceptInvite(invite.Token, "friend", "password123"); err == nil {
|
|
t.Fatalf("invite token remained reusable")
|
|
}
|
|
}
|
|
|
|
func TestNonOwnerCannotManageOwnedBox(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
|
|
owner, err := app.authService.CreateBootstrapUser("owner", "owner@example.test", "password123")
|
|
if err != nil {
|
|
t.Fatalf("CreateBootstrapUser returned error: %v", err)
|
|
}
|
|
invite, err := app.authService.CreateInvite("other@example.test", services.UserRoleUser, owner.ID, 0)
|
|
if err != nil {
|
|
t.Fatalf("CreateInvite returned error: %v", err)
|
|
}
|
|
other, err := app.authService.AcceptInvite(invite.Token, "other", "password123")
|
|
if err != nil {
|
|
t.Fatalf("AcceptInvite returned error: %v", err)
|
|
}
|
|
|
|
result := createOwnedBoxThroughApp(t, app, owner.ID)
|
|
if err := app.uploadService.RenameOwnedBox(result.BoxID, other.ID, "stolen"); err == nil {
|
|
t.Fatalf("RenameOwnedBox allowed non-owner")
|
|
}
|
|
}
|
|
|
|
func TestAdminUploadBypassesMaxUploadSize(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
|
|
_, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
|
|
if err != nil {
|
|
t.Fatalf("CreateBootstrapUser returned error: %v", err)
|
|
}
|
|
_, token, err := app.authService.Login("admin@example.test", "password123")
|
|
if err != nil {
|
|
t.Fatalf("Login returned error: %v", err)
|
|
}
|
|
|
|
request := multipartUploadRequest(t, "/api/v1/upload", "file", "large.txt", strings.Repeat("x", int(app.uploadService.MaxUploadSize())+1))
|
|
request.Header.Set("Accept", "application/json")
|
|
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
|
|
response := httptest.NewRecorder()
|
|
app.Upload(response, request)
|
|
if response.Code != http.StatusCreated {
|
|
t.Fatalf("admin upload status = %d, body = %s", response.Code, response.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestAnonymousUploadDisabled(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
policy := testPolicy(t, app)
|
|
policy.AnonymousUploadsEnabled = false
|
|
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
|
|
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
|
|
}
|
|
|
|
request := multipartUploadRequest(t, "/api/v1/upload", "file", "note.txt", "hello")
|
|
request.Header.Set("Accept", "application/json")
|
|
response := httptest.NewRecorder()
|
|
app.Upload(response, request)
|
|
if response.Code != http.StatusForbidden {
|
|
t.Fatalf("status = %d, want 403, body = %s", response.Code, response.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestAnonymousUploadLimits(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
policy := testPolicy(t, app)
|
|
policy.AnonymousMaxUploadMB = 1
|
|
policy.AnonymousDailyUploadMB = 0.001
|
|
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
|
|
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
|
|
}
|
|
|
|
large := multipartUploadRequest(t, "/api/v1/upload", "file", "large.txt", strings.Repeat("x", 2*1024*1024))
|
|
large.Header.Set("Accept", "application/json")
|
|
large.RemoteAddr = "192.0.2.10:1234"
|
|
largeResponse := httptest.NewRecorder()
|
|
app.Upload(largeResponse, large)
|
|
if largeResponse.Code != http.StatusRequestEntityTooLarge {
|
|
t.Fatalf("large status = %d, body = %s", largeResponse.Code, largeResponse.Body.String())
|
|
}
|
|
|
|
daily := multipartUploadRequest(t, "/api/v1/upload", "file", "note.txt", strings.Repeat("x", 2048))
|
|
daily.Header.Set("Accept", "application/json")
|
|
daily.RemoteAddr = "192.0.2.10:1234"
|
|
dailyResponse := httptest.NewRecorder()
|
|
app.Upload(dailyResponse, daily)
|
|
if dailyResponse.Code != http.StatusTooManyRequests {
|
|
t.Fatalf("daily status = %d, body = %s", dailyResponse.Code, dailyResponse.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestSignedInUploadQuotaAndOverride(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
user, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
|
|
if err != nil {
|
|
t.Fatalf("CreateBootstrapUser returned error: %v", err)
|
|
}
|
|
invite, err := app.authService.CreateInvite("user@example.test", services.UserRoleUser, user.ID, 0)
|
|
if err != nil {
|
|
t.Fatalf("CreateInvite returned error: %v", err)
|
|
}
|
|
normal, err := app.authService.AcceptInvite(invite.Token, "user", "password123")
|
|
if err != nil {
|
|
t.Fatalf("AcceptInvite returned error: %v", err)
|
|
}
|
|
_, token, err := app.authService.Login(normal.Email, "password123")
|
|
if err != nil {
|
|
t.Fatalf("Login returned error: %v", err)
|
|
}
|
|
policy := testPolicy(t, app)
|
|
policy.DefaultUserStorageMB = 0.001
|
|
policy.UserDailyUploadMB = 8
|
|
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
|
|
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
|
|
}
|
|
|
|
request := multipartUploadRequest(t, "/api/v1/upload", "file", "quota.txt", strings.Repeat("x", 2048))
|
|
request.Header.Set("Accept", "application/json")
|
|
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
|
|
response := httptest.NewRecorder()
|
|
app.Upload(response, request)
|
|
if response.Code != http.StatusRequestEntityTooLarge {
|
|
t.Fatalf("quota status = %d, body = %s", response.Code, response.Body.String())
|
|
}
|
|
|
|
override := 10.0
|
|
if err := app.authService.SetUserStorageQuota(normal.ID, &override); err != nil {
|
|
t.Fatalf("SetUserStorageQuota returned error: %v", err)
|
|
}
|
|
request = multipartUploadRequest(t, "/api/v1/upload", "file", "quota.txt", strings.Repeat("x", 2048))
|
|
request.Header.Set("Accept", "application/json")
|
|
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
|
|
response = httptest.NewRecorder()
|
|
app.Upload(response, request)
|
|
if response.Code != http.StatusCreated {
|
|
t.Fatalf("override status = %d, body = %s", response.Code, response.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestSignedInDailyCap(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
admin, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
|
|
if err != nil {
|
|
t.Fatalf("CreateBootstrapUser returned error: %v", err)
|
|
}
|
|
invite, err := app.authService.CreateInvite("user@example.test", services.UserRoleUser, admin.ID, 0)
|
|
if err != nil {
|
|
t.Fatalf("CreateInvite returned error: %v", err)
|
|
}
|
|
user, err := app.authService.AcceptInvite(invite.Token, "user", "password123")
|
|
if err != nil {
|
|
t.Fatalf("AcceptInvite returned error: %v", err)
|
|
}
|
|
_, token, err := app.authService.Login(user.Email, "password123")
|
|
if err != nil {
|
|
t.Fatalf("Login returned error: %v", err)
|
|
}
|
|
policy := testPolicy(t, app)
|
|
policy.UserDailyUploadMB = 0.001
|
|
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
|
|
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
|
|
}
|
|
request := multipartUploadRequest(t, "/api/v1/upload", "file", "daily.txt", strings.Repeat("x", 2048))
|
|
request.Header.Set("Accept", "application/json")
|
|
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
|
|
response := httptest.NewRecorder()
|
|
app.Upload(response, request)
|
|
if response.Code != http.StatusTooManyRequests {
|
|
t.Fatalf("daily status = %d, body = %s", response.Code, response.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestLayeredUploadLimits(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
policy := testPolicy(t, app)
|
|
policy.AnonymousDailyBoxes = 1
|
|
policy.AnonymousActiveBoxes = 10
|
|
policy.AnonymousMaxDays = 3
|
|
policy.LocalStorageMaxGB = 0.001
|
|
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
|
|
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
|
|
}
|
|
|
|
first := uploadThroughApp(t, app)
|
|
if first.BoxID == "" {
|
|
t.Fatalf("first upload did not return a box id")
|
|
}
|
|
secondRequest := multipartUploadRequest(t, "/api/v1/upload", "file", "second.txt", "hello")
|
|
secondRequest.Header.Set("Accept", "application/json")
|
|
secondResponse := httptest.NewRecorder()
|
|
app.Upload(secondResponse, secondRequest)
|
|
if secondResponse.Code != http.StatusTooManyRequests {
|
|
t.Fatalf("daily box status = %d, body = %s", secondResponse.Code, secondResponse.Body.String())
|
|
}
|
|
|
|
policy.AnonymousDailyBoxes = 10
|
|
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
|
|
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
|
|
}
|
|
expiryRequest := multipartUploadRequestWithField(t, "/api/v1/upload", "file", "expiry.txt", "hello", "max_days", "30")
|
|
expiryRequest.Header.Set("Accept", "application/json")
|
|
expiryResponse := httptest.NewRecorder()
|
|
app.Upload(expiryResponse, expiryRequest)
|
|
if expiryResponse.Code != http.StatusRequestEntityTooLarge && expiryResponse.Code != http.StatusTooManyRequests {
|
|
t.Fatalf("expiry/box status = %d, body = %s", expiryResponse.Code, expiryResponse.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestUserPolicyOverrideChangesUploadEnforcement(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
admin, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
|
|
if err != nil {
|
|
t.Fatalf("CreateBootstrapUser returned error: %v", err)
|
|
}
|
|
invite, err := app.authService.CreateInvite("user@example.test", services.UserRoleUser, admin.ID, 0)
|
|
if err != nil {
|
|
t.Fatalf("CreateInvite returned error: %v", err)
|
|
}
|
|
user, err := app.authService.AcceptInvite(invite.Token, "user", "password123")
|
|
if err != nil {
|
|
t.Fatalf("AcceptInvite returned error: %v", err)
|
|
}
|
|
dailyBoxes := 1
|
|
maxDays := 1
|
|
if err := app.authService.SetUserPolicy(user.ID, services.UserPolicy{DailyBoxes: &dailyBoxes, MaxDays: &maxDays}); err != nil {
|
|
t.Fatalf("SetUserPolicy returned error: %v", err)
|
|
}
|
|
_, token, err := app.authService.Login(user.Email, "password123")
|
|
if err != nil {
|
|
t.Fatalf("Login returned error: %v", err)
|
|
}
|
|
first := multipartUploadRequest(t, "/api/v1/upload", "file", "one.txt", "hello")
|
|
first.Header.Set("Accept", "application/json")
|
|
first.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
|
|
firstResponse := httptest.NewRecorder()
|
|
app.Upload(firstResponse, first)
|
|
if firstResponse.Code != http.StatusCreated {
|
|
t.Fatalf("first status = %d, body = %s", firstResponse.Code, firstResponse.Body.String())
|
|
}
|
|
second := multipartUploadRequest(t, "/api/v1/upload", "file", "two.txt", "hello")
|
|
second.Header.Set("Accept", "application/json")
|
|
second.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
|
|
secondResponse := httptest.NewRecorder()
|
|
app.Upload(secondResponse, second)
|
|
if secondResponse.Code != http.StatusTooManyRequests {
|
|
t.Fatalf("second status = %d, body = %s", secondResponse.Code, secondResponse.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestLocalStorageCapRejectsUpload(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
policy := testPolicy(t, app)
|
|
policy.AnonymousMaxUploadMB = 4
|
|
policy.AnonymousDailyUploadMB = 8
|
|
policy.LocalStorageMaxGB = 0.001
|
|
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
|
|
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
|
|
}
|
|
request := multipartUploadRequest(t, "/api/v1/upload", "file", "large.txt", strings.Repeat("x", 2*1024*1024))
|
|
request.Header.Set("Accept", "application/json")
|
|
response := httptest.NewRecorder()
|
|
app.Upload(response, request)
|
|
if response.Code != http.StatusRequestEntityTooLarge {
|
|
t.Fatalf("storage cap status = %d, body = %s", response.Code, response.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestAdminSettingsPostChangesUploadEnforcement(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
_, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
|
|
if err != nil {
|
|
t.Fatalf("CreateBootstrapUser returned error: %v", err)
|
|
}
|
|
_, token, err := app.authService.Login("admin@example.test", "password123")
|
|
if err != nil {
|
|
t.Fatalf("Login returned error: %v", err)
|
|
}
|
|
|
|
settingsForm := strings.NewReader("anonymous_max_upload_mb=512&anonymous_daily_upload_mb=2048&user_daily_upload_mb=8192&default_user_storage_mb=51200&usage_retention_days=30&csrf_token=test-csrf")
|
|
settingsRequest := httptest.NewRequest(http.MethodPost, "/admin/settings", settingsForm)
|
|
settingsRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
settingsRequest.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
|
|
settingsRequest.AddCookie(&http.Cookie{Name: csrfCookieName, Value: "test-csrf"})
|
|
settingsResponse := httptest.NewRecorder()
|
|
app.AdminSettingsPost(settingsResponse, settingsRequest)
|
|
if settingsResponse.Code != http.StatusSeeOther {
|
|
t.Fatalf("AdminSettingsPost status = %d, body = %s", settingsResponse.Code, settingsResponse.Body.String())
|
|
}
|
|
|
|
uploadRequest := multipartUploadRequest(t, "/api/v1/upload", "file", "note.txt", "hello")
|
|
uploadRequest.Header.Set("Accept", "application/json")
|
|
uploadResponse := httptest.NewRecorder()
|
|
app.Upload(uploadResponse, uploadRequest)
|
|
if uploadResponse.Code != http.StatusForbidden {
|
|
t.Fatalf("upload status = %d, want 403, body = %s", uploadResponse.Code, uploadResponse.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestAdminUserQuotaPostChangesEnforcement(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
admin, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
|
|
if err != nil {
|
|
t.Fatalf("CreateBootstrapUser returned error: %v", err)
|
|
}
|
|
invite, err := app.authService.CreateInvite("user@example.test", services.UserRoleUser, admin.ID, 0)
|
|
if err != nil {
|
|
t.Fatalf("CreateInvite returned error: %v", err)
|
|
}
|
|
user, err := app.authService.AcceptInvite(invite.Token, "user", "password123")
|
|
if err != nil {
|
|
t.Fatalf("AcceptInvite returned error: %v", err)
|
|
}
|
|
_, adminToken, err := app.authService.Login(admin.Email, "password123")
|
|
if err != nil {
|
|
t.Fatalf("admin Login returned error: %v", err)
|
|
}
|
|
|
|
quotaRequest := httptest.NewRequest(http.MethodPost, "/admin/users/"+user.ID+"/quota", strings.NewReader("storage_quota_mb=0.001&csrf_token=test-csrf"))
|
|
quotaRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
quotaRequest.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: adminToken})
|
|
quotaRequest.AddCookie(&http.Cookie{Name: csrfCookieName, Value: "test-csrf"})
|
|
quotaRequest.SetPathValue("userID", user.ID)
|
|
quotaResponse := httptest.NewRecorder()
|
|
app.AdminUpdateUserQuota(quotaResponse, quotaRequest)
|
|
if quotaResponse.Code != http.StatusSeeOther {
|
|
t.Fatalf("AdminUpdateUserQuota status = %d, body = %s", quotaResponse.Code, quotaResponse.Body.String())
|
|
}
|
|
|
|
_, userToken, err := app.authService.Login(user.Email, "password123")
|
|
if err != nil {
|
|
t.Fatalf("user Login returned error: %v", err)
|
|
}
|
|
uploadRequest := multipartUploadRequest(t, "/api/v1/upload", "file", "quota.txt", strings.Repeat("x", 2048))
|
|
uploadRequest.Header.Set("Accept", "application/json")
|
|
uploadRequest.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: userToken})
|
|
uploadResponse := httptest.NewRecorder()
|
|
app.Upload(uploadResponse, uploadRequest)
|
|
if uploadResponse.Code != http.StatusRequestEntityTooLarge {
|
|
t.Fatalf("upload status = %d, want 413, body = %s", uploadResponse.Code, uploadResponse.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestHomeReflectsUploadPolicySettings(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
policy := testPolicy(t, app)
|
|
policy.AnonymousMaxUploadMB = 123
|
|
policy.AnonymousDailyUploadMB = 456
|
|
if err := app.settingsService.UpdateUploadPolicy(policy); err != nil {
|
|
t.Fatalf("UpdateUploadPolicy returned error: %v", err)
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
response := httptest.NewRecorder()
|
|
app.Home(response, request)
|
|
if response.Code != http.StatusOK {
|
|
t.Fatalf("Home status = %d", response.Code)
|
|
}
|
|
body := response.Body.String()
|
|
if !strings.Contains(body, "Max file size: 123 MB") || !strings.Contains(body, "456 MB") {
|
|
t.Fatalf("home did not reflect policy settings: %s", body)
|
|
}
|
|
}
|
|
|
|
func TestAPIDocsHeaderReflectsLoggedInUser(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
_, err := app.authService.CreateBootstrapUser("admin", "admin@example.test", "password123")
|
|
if err != nil {
|
|
t.Fatalf("CreateBootstrapUser returned error: %v", err)
|
|
}
|
|
_, token, err := app.authService.Login("admin@example.test", "password123")
|
|
if err != nil {
|
|
t.Fatalf("Login returned error: %v", err)
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "/api", nil)
|
|
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
|
|
response := httptest.NewRecorder()
|
|
app.APIDocs(response, request)
|
|
if response.Code != http.StatusOK {
|
|
t.Fatalf("APIDocs status = %d", response.Code)
|
|
}
|
|
body := response.Body.String()
|
|
header := body[:strings.Index(body, "<main")]
|
|
if !strings.Contains(header, "Dashboard") || strings.Contains(header, "Sign in") || strings.Contains(header, "Health") {
|
|
t.Fatalf("api header did not reflect logged-in state: %s", body)
|
|
}
|
|
}
|
|
|
|
func TestAPIDocsHeaderReflectsLoggedOutUser(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "/api", nil)
|
|
response := httptest.NewRecorder()
|
|
app.APIDocs(response, request)
|
|
if response.Code != http.StatusOK {
|
|
t.Fatalf("APIDocs status = %d", response.Code)
|
|
}
|
|
body := response.Body.String()
|
|
header := body[:strings.Index(body, "<main")]
|
|
if !strings.Contains(header, "Sign in") || !strings.Contains(header, ">API<") || strings.Contains(header, "Health") || strings.Contains(header, "Dashboard") {
|
|
t.Fatalf("api header did not reflect logged-out state: %s", body)
|
|
}
|
|
}
|
|
|
|
func createOwnedBoxThroughApp(t *testing.T, app *App, userID string) services.UploadResult {
|
|
t.Helper()
|
|
user, err := app.authService.UserByID(userID)
|
|
if err != nil {
|
|
t.Fatalf("UserByID returned error: %v", err)
|
|
}
|
|
_, token, err := app.authService.Login(user.Email, "password123")
|
|
if err != nil {
|
|
t.Fatalf("Login returned error: %v", err)
|
|
}
|
|
request := multipartUploadRequest(t, "/api/v1/upload", "file", "owned.txt", "owned")
|
|
request.Header.Set("Accept", "application/json")
|
|
request.AddCookie(&http.Cookie{Name: userSessionCookieName, Value: token})
|
|
response := httptest.NewRecorder()
|
|
app.Upload(response, request)
|
|
if response.Code != http.StatusCreated {
|
|
t.Fatalf("upload status = %d, body = %s", response.Code, response.Body.String())
|
|
}
|
|
var payload services.UploadResult
|
|
if err := json.Unmarshal(response.Body.Bytes(), &payload); err != nil {
|
|
t.Fatalf("json.Unmarshal returned error: %v", err)
|
|
}
|
|
return payload
|
|
}
|
|
|
|
func testPolicy(t *testing.T, app *App) services.UploadPolicySettings {
|
|
t.Helper()
|
|
policy, err := app.settingsService.UploadPolicy()
|
|
if err != nil {
|
|
t.Fatalf("UploadPolicy returned error: %v", err)
|
|
}
|
|
return policy
|
|
}
|