Modify the authentication handler to return an unauthorized error when an invalid or disabled bearer token is provided, rather than silently falling back to an anonymous request. This ensures that clients attempting to authenticate but failing (due to expired, malformed, or disabled tokens) are explicitly notified of the auth failure instead of proceeding anonymously. True anonymous requests without any Authorization header remain supported.
626 lines
24 KiB
Go
626 lines
24 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 is an authentication failure, not an anonymous upload.
|
|
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.StatusUnauthorized {
|
|
t.Fatalf("invalid token upload status = %d, body = %s", badResponse.Code, badResponse.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestAnonymousUploadWithoutBearerStillWorks(t *testing.T) {
|
|
app, cleanup := newTestApp(t)
|
|
defer cleanup()
|
|
|
|
response := httptest.NewRecorder()
|
|
app.Upload(response, multipartUploadRequest(t, "/api/v1/upload", "file", "anonymous.txt", "anonymous"))
|
|
if response.Code != http.StatusCreated {
|
|
t.Fatalf("anonymous upload status = %d, body = %s", response.Code, response.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestDisabledUserBearerTokenCannotUpload(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)
|
|
}
|
|
if err := app.authService.DisableUser(user.ID, true); err != nil {
|
|
t.Fatalf("DisableUser returned error: %v", err)
|
|
}
|
|
|
|
request := multipartUploadRequest(t, "/api/v1/upload", "file", "blocked.txt", "blocked")
|
|
request.Header.Set("Accept", "application/json")
|
|
request.Header.Set("Authorization", "Bearer "+tokenResult.Plaintext)
|
|
response := httptest.NewRecorder()
|
|
app.Upload(response, request)
|
|
if response.Code != http.StatusUnauthorized {
|
|
t.Fatalf("disabled bearer upload status = %d, body = %s", response.Code, response.Body.String())
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|