Adds configuration options and environment variables to manage box owner policies, including settings for refresh counts and expiry.
246 lines
7.6 KiB
Go
246 lines
7.6 KiB
Go
package server
|
|
|
|
import (
|
|
"html/template"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"warpbox/lib/boxstore"
|
|
"warpbox/lib/config"
|
|
"warpbox/lib/metastore"
|
|
)
|
|
|
|
func TestAccountLoginSuccess(t *testing.T) {
|
|
app, _ := setupAccountTestApp(t)
|
|
router := setupAccountTestRouter(t, app)
|
|
|
|
response := postAccountLogin(router, "admin", "secret")
|
|
if response.Code != http.StatusSeeOther {
|
|
t.Fatalf("expected login redirect, got %d", response.Code)
|
|
}
|
|
if location := response.Header().Get("Location"); location != "/account" {
|
|
t.Fatalf("expected redirect to /account, got %q", location)
|
|
}
|
|
if cookie := findResponseCookie(response, accountSessionCookie); cookie == nil || cookie.Value == "" {
|
|
t.Fatal("expected account session cookie")
|
|
}
|
|
}
|
|
|
|
func TestAccountLoginFailure(t *testing.T) {
|
|
app, _ := setupAccountTestApp(t)
|
|
router := setupAccountTestRouter(t, app)
|
|
|
|
response := postAccountLogin(router, "admin", "wrong")
|
|
if response.Code != http.StatusOK {
|
|
t.Fatalf("expected failed login to render form, got %d", response.Code)
|
|
}
|
|
if cookie := findResponseCookie(response, accountSessionCookie); cookie != nil {
|
|
t.Fatal("did not expect account session cookie")
|
|
}
|
|
if !strings.Contains(response.Body.String(), "not accepted") {
|
|
t.Fatal("expected login failure message")
|
|
}
|
|
}
|
|
|
|
func TestAccountDisabledUserLoginFailure(t *testing.T) {
|
|
app, user := setupAccountTestApp(t)
|
|
user.Disabled = true
|
|
if err := app.store.UpdateUser(user); err != nil {
|
|
t.Fatalf("UpdateUser returned error: %v", err)
|
|
}
|
|
router := setupAccountTestRouter(t, app)
|
|
|
|
response := postAccountLogin(router, "admin", "secret")
|
|
if response.Code != http.StatusOK {
|
|
t.Fatalf("expected disabled login to render form, got %d", response.Code)
|
|
}
|
|
if cookie := findResponseCookie(response, accountSessionCookie); cookie != nil {
|
|
t.Fatal("did not expect account session cookie")
|
|
}
|
|
if !strings.Contains(response.Body.String(), "not accepted") {
|
|
t.Fatal("expected login failure message")
|
|
}
|
|
}
|
|
|
|
func TestAccountLogoutRequiresCSRF(t *testing.T) {
|
|
app, user := setupAccountTestApp(t)
|
|
router := setupAccountTestRouter(t, app)
|
|
session, err := app.store.CreateSession(user.ID, time.Hour)
|
|
if err != nil {
|
|
t.Fatalf("CreateSession returned error: %v", err)
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodPost, "/account/logout", 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 missing CSRF token to be forbidden, got %d", response.Code)
|
|
}
|
|
}
|
|
|
|
func TestAccountDashboardRequiresAuth(t *testing.T) {
|
|
app, _ := setupAccountTestApp(t)
|
|
router := setupAccountTestRouter(t, app)
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "/account", nil)
|
|
response := httptest.NewRecorder()
|
|
router.ServeHTTP(response, request)
|
|
if response.Code != http.StatusSeeOther {
|
|
t.Fatalf("expected dashboard redirect, got %d", response.Code)
|
|
}
|
|
if location := response.Header().Get("Location"); location != "/account/login" {
|
|
t.Fatalf("expected redirect to /account/login, got %q", location)
|
|
}
|
|
}
|
|
|
|
func TestAccountDashboardLoadsForBootstrapAdmin(t *testing.T) {
|
|
app, user := setupAccountTestApp(t)
|
|
router := setupAccountTestRouter(t, app)
|
|
session, err := app.store.CreateSession(user.ID, time.Hour)
|
|
if err != nil {
|
|
t.Fatalf("CreateSession returned error: %v", err)
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "/account", 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 dashboard to load, got %d", response.Code)
|
|
}
|
|
body := response.Body.String()
|
|
for _, text := range []string{"Dashboard", "Recent Boxes", "Users"} {
|
|
if !strings.Contains(body, text) {
|
|
t.Fatalf("expected dashboard body to contain %q", text)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAccountDashboardHidesAdminOnlyLinksForRegularUser(t *testing.T) {
|
|
app, _ := setupAccountTestApp(t)
|
|
user, err := app.store.CreateUserWithPassword("maya", "maya@example.test", "secret", nil)
|
|
if err != nil {
|
|
t.Fatalf("CreateUserWithPassword returned error: %v", err)
|
|
}
|
|
router := setupAccountTestRouter(t, app)
|
|
session, err := app.store.CreateSession(user.ID, time.Hour)
|
|
if err != nil {
|
|
t.Fatalf("CreateSession returned error: %v", err)
|
|
}
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "/account", 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 dashboard to load, got %d", response.Code)
|
|
}
|
|
body := response.Body.String()
|
|
for _, text := range []string{">Users<", ">Settings<"} {
|
|
if strings.Contains(body, text) {
|
|
t.Fatalf("expected dashboard body to hide %q", text)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAdminEntryRedirectsToAccount(t *testing.T) {
|
|
app, _ := setupAccountTestApp(t)
|
|
router := setupAccountTestRouter(t, app)
|
|
|
|
cases := map[string]string{
|
|
"/admin/login": "/account/login",
|
|
"/admin": "/account",
|
|
}
|
|
for path, wantLocation := range cases {
|
|
request := httptest.NewRequest(http.MethodGet, path, nil)
|
|
response := httptest.NewRecorder()
|
|
router.ServeHTTP(response, request)
|
|
if response.Code != http.StatusSeeOther {
|
|
t.Fatalf("expected %s redirect, got %d", path, response.Code)
|
|
}
|
|
if location := response.Header().Get("Location"); location != wantLocation {
|
|
t.Fatalf("expected %s to redirect to %s, got %q", path, wantLocation, location)
|
|
}
|
|
}
|
|
}
|
|
|
|
func setupAccountTestApp(t *testing.T) (*App, metastore.User) {
|
|
t.Helper()
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
restoreUploadRoot := boxstore.UploadRoot()
|
|
t.Cleanup(func() { boxstore.SetUploadRoot(restoreUploadRoot) })
|
|
boxstore.SetUploadRoot(t.TempDir())
|
|
|
|
store, err := metastore.Open(t.TempDir())
|
|
if err != nil {
|
|
t.Fatalf("Open returned error: %v", err)
|
|
}
|
|
t.Cleanup(func() { _ = store.Close() })
|
|
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
t.Fatalf("Load returned error: %v", err)
|
|
}
|
|
cfg.AdminUsername = "admin"
|
|
cfg.AdminPassword = "secret"
|
|
cfg.AdminEmail = "admin@example.test"
|
|
cfg.AdminEnabled = config.AdminEnabledAuto
|
|
cfg.SessionTTLSeconds = 3600
|
|
bootstrap, err := metastore.BootstrapAdmin(cfg, store)
|
|
if err != nil {
|
|
t.Fatalf("BootstrapAdmin returned error: %v", err)
|
|
}
|
|
if bootstrap.AdminUser == nil {
|
|
t.Fatal("expected bootstrap admin user")
|
|
}
|
|
|
|
app := &App{
|
|
config: cfg,
|
|
store: store,
|
|
adminLoginEnabled: bootstrap.AdminLoginEnabled,
|
|
}
|
|
return app, *bootstrap.AdminUser
|
|
}
|
|
|
|
func setupAccountTestRouter(t *testing.T, app *App) *gin.Engine {
|
|
t.Helper()
|
|
router := gin.New()
|
|
templates, err := template.ParseGlob(filepath.Join("..", "..", "templates", "*.html"))
|
|
if err != nil {
|
|
t.Fatalf("ParseGlob returned error: %v", err)
|
|
}
|
|
router.SetHTMLTemplate(templates)
|
|
app.registerAccountRoutes(router)
|
|
app.registerAdminRoutes(router)
|
|
return router
|
|
}
|
|
|
|
func postAccountLogin(router *gin.Engine, username string, password string) *httptest.ResponseRecorder {
|
|
form := url.Values{}
|
|
form.Set("username", username)
|
|
form.Set("password", password)
|
|
request := httptest.NewRequest(http.MethodPost, "/account/login", strings.NewReader(form.Encode()))
|
|
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
response := httptest.NewRecorder()
|
|
router.ServeHTTP(response, request)
|
|
return response
|
|
}
|
|
|
|
func findResponseCookie(response *httptest.ResponseRecorder, name string) *http.Cookie {
|
|
for _, cookie := range response.Result().Cookies() {
|
|
if cookie.Name == name {
|
|
return cookie
|
|
}
|
|
}
|
|
return nil
|
|
}
|