Add users and network settings tabs with admin protections.

List and delete accounts with last-admin and Administrators-group guards, and expose editable network settings via the configuration UI.
This commit is contained in:
2026-07-18 15:18:01 +02:00
parent f049f766d9
commit 2605c3b346
49 changed files with 5440 additions and 209 deletions
+12 -17
View File
@@ -15,12 +15,11 @@ type securityResponseTest struct {
}
func TestSecurityGetReturnsDefaultsWhenMissing(t *testing.T) {
t.Parallel()
configDir := t.TempDir()
cookie := seedCompletedSetup(t, configDir)
router := NewRouter(configDir)
request := httptest.NewRequest(http.MethodGet, "/api/v1/security", nil)
request := withSession(httptest.NewRequest(http.MethodGet, "/api/v1/security", nil), cookie)
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
@@ -40,9 +39,8 @@ func TestSecurityGetReturnsDefaultsWhenMissing(t *testing.T) {
}
func TestSecurityPutRejectsIdleTimeoutZero(t *testing.T) {
t.Parallel()
configDir := t.TempDir()
cookie := seedCompletedSetup(t, configDir)
router := NewRouter(configDir)
body := []byte(`{
@@ -53,7 +51,7 @@ func TestSecurityPutRejectsIdleTimeoutZero(t *testing.T) {
"reauth_grace_minutes": 15
}
}`)
request := httptest.NewRequest(http.MethodPut, "/api/v1/security", bytes.NewReader(body))
request := withSession(httptest.NewRequest(http.MethodPut, "/api/v1/security", bytes.NewReader(body)), cookie)
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
@@ -63,9 +61,8 @@ func TestSecurityPutRejectsIdleTimeoutZero(t *testing.T) {
}
func TestSecurityPutRejectsIdleTimeoutAboveMax(t *testing.T) {
t.Parallel()
configDir := t.TempDir()
cookie := seedCompletedSetup(t, configDir)
router := NewRouter(configDir)
body := []byte(`{
@@ -76,7 +73,7 @@ func TestSecurityPutRejectsIdleTimeoutAboveMax(t *testing.T) {
"reauth_grace_minutes": 15
}
}`)
request := httptest.NewRequest(http.MethodPut, "/api/v1/security", bytes.NewReader(body))
request := withSession(httptest.NewRequest(http.MethodPut, "/api/v1/security", bytes.NewReader(body)), cookie)
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
@@ -86,9 +83,8 @@ func TestSecurityPutRejectsIdleTimeoutAboveMax(t *testing.T) {
}
func TestSecurityPutRejectsSessionLifetimeZero(t *testing.T) {
t.Parallel()
configDir := t.TempDir()
cookie := seedCompletedSetup(t, configDir)
router := NewRouter(configDir)
body := []byte(`{
@@ -99,7 +95,7 @@ func TestSecurityPutRejectsSessionLifetimeZero(t *testing.T) {
"reauth_grace_minutes": 15
}
}`)
request := httptest.NewRequest(http.MethodPut, "/api/v1/security", bytes.NewReader(body))
request := withSession(httptest.NewRequest(http.MethodPut, "/api/v1/security", bytes.NewReader(body)), cookie)
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
@@ -109,9 +105,8 @@ func TestSecurityPutRejectsSessionLifetimeZero(t *testing.T) {
}
func TestSecurityPutRoundTrip(t *testing.T) {
t.Parallel()
configDir := t.TempDir()
cookie := seedCompletedSetup(t, configDir)
router := NewRouter(configDir)
body := []byte(`{
@@ -123,15 +118,15 @@ func TestSecurityPutRoundTrip(t *testing.T) {
"totp_enabled": true
}
}`)
putReq := httptest.NewRequest(http.MethodPut, "/api/v1/security", bytes.NewReader(body))
putReq := withSession(httptest.NewRequest(http.MethodPut, "/api/v1/security", bytes.NewReader(body)), cookie)
putRec := httptest.NewRecorder()
router.ServeHTTP(putRec, putReq)
if putRec.Code != http.StatusOK {
t.Fatalf("expected status %d, got %d", http.StatusOK, putRec.Code)
t.Fatalf("expected status %d, got %d body %s", http.StatusOK, putRec.Code, putRec.Body.String())
}
getReq := httptest.NewRequest(http.MethodGet, "/api/v1/security", nil)
getReq := withSession(httptest.NewRequest(http.MethodGet, "/api/v1/security", nil), cookie)
getRec := httptest.NewRecorder()
router.ServeHTTP(getRec, getReq)