Add activity logs, sidebar badges, and logout with working alert colors.
Record auth audits and surface them in Activity, poll sidebar counts without resetting idle, and add a profile logout path plus theme red/green/blue tokens so failure badges render.
This commit is contained in:
@@ -360,13 +360,32 @@ func (app *App) loginHandler(writer http.ResponseWriter, request *http.Request)
|
||||
break
|
||||
}
|
||||
}
|
||||
attemptedUsername := strings.TrimSpace(payload.Username)
|
||||
if matched == nil || !matched.Enabled {
|
||||
appendAuthAudit(
|
||||
app.ConfigDir,
|
||||
"login",
|
||||
settings.AuthAuditOutcomeFailure,
|
||||
settings.AuthAuditCategoryAuth,
|
||||
attemptedUsername,
|
||||
attemptedUsername,
|
||||
"invalid credentials",
|
||||
)
|
||||
writeJSON(writer, http.StatusUnauthorized, apiErrorResponse{Error: "invalid credentials"})
|
||||
return
|
||||
}
|
||||
|
||||
ok, err := auth.VerifyPassword(payload.Password, matched.PasswordHash)
|
||||
if err != nil || !ok {
|
||||
appendAuthAudit(
|
||||
app.ConfigDir,
|
||||
"login",
|
||||
settings.AuthAuditOutcomeFailure,
|
||||
settings.AuthAuditCategoryAuth,
|
||||
attemptedUsername,
|
||||
matched.Username,
|
||||
"invalid credentials",
|
||||
)
|
||||
writeJSON(writer, http.StatusUnauthorized, apiErrorResponse{Error: "invalid credentials"})
|
||||
return
|
||||
}
|
||||
@@ -381,6 +400,15 @@ func (app *App) loginHandler(writer http.ResponseWriter, request *http.Request)
|
||||
requiresTOTP := security.TotpEnabled && matched.TOTPConfirmed && matched.TOTPSecret != ""
|
||||
if requiresTOTP {
|
||||
if !auth.VerifyTOTPCode(matched.TOTPSecret, payload.TOTPCode) {
|
||||
appendAuthAudit(
|
||||
app.ConfigDir,
|
||||
"login",
|
||||
settings.AuthAuditOutcomeFailure,
|
||||
settings.AuthAuditCategoryAuth,
|
||||
matched.Username,
|
||||
matched.Username,
|
||||
"invalid or missing TOTP code",
|
||||
)
|
||||
writeJSON(writer, http.StatusUnauthorized, apiErrorResponse{Error: "invalid or missing TOTP code"})
|
||||
return
|
||||
}
|
||||
@@ -391,13 +419,34 @@ func (app *App) loginHandler(writer http.ResponseWriter, request *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
appendAuthAudit(
|
||||
app.ConfigDir,
|
||||
"login",
|
||||
settings.AuthAuditOutcomeSuccess,
|
||||
settings.AuthAuditCategoryAuth,
|
||||
matched.Username,
|
||||
matched.Username,
|
||||
"",
|
||||
)
|
||||
writeJSON(writer, http.StatusOK, app.buildMeResponse(*matched, settingsPayload))
|
||||
}
|
||||
|
||||
func (app *App) logoutHandler(writer http.ResponseWriter, request *http.Request) {
|
||||
actor := actorUsernameFromRequest(request)
|
||||
if app.Sessions != nil {
|
||||
_ = app.Sessions.DestroySession(writer, request)
|
||||
}
|
||||
if actor != "" {
|
||||
appendAuthAudit(
|
||||
app.ConfigDir,
|
||||
"logout",
|
||||
settings.AuthAuditOutcomeSuccess,
|
||||
settings.AuthAuditCategoryAuth,
|
||||
actor,
|
||||
actor,
|
||||
"",
|
||||
)
|
||||
}
|
||||
writeJSON(writer, http.StatusOK, map[string]bool{"ok": true})
|
||||
}
|
||||
|
||||
@@ -459,6 +508,7 @@ func allPermissionList() []string {
|
||||
"users.manage",
|
||||
"secrets.manage",
|
||||
"roles.manage",
|
||||
"logs.read",
|
||||
}
|
||||
_ = list
|
||||
return ordered
|
||||
@@ -484,5 +534,35 @@ func loadSettingsOrDefault(configDir string) (settings.Settings, error) {
|
||||
payload.Groups = []settings.Group{}
|
||||
}
|
||||
payload.Network = settings.EffectiveNetwork(payload.Network)
|
||||
if migrateLogsReadPermission(&payload) {
|
||||
if err := settings.SaveSettings(configDir, payload); err != nil {
|
||||
return settings.Settings{}, err
|
||||
}
|
||||
}
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
// migrateLogsReadPermission grants logs.read to groups that previously could
|
||||
// view logs via nodes.read or users.manage. Returns true when settings changed.
|
||||
func migrateLogsReadPermission(payload *settings.Settings) bool {
|
||||
changed := false
|
||||
for index := range payload.Groups {
|
||||
group := &payload.Groups[index]
|
||||
hasLogsRead := false
|
||||
hadLogAccess := false
|
||||
for _, permission := range group.Permissions {
|
||||
switch permission {
|
||||
case "logs.read":
|
||||
hasLogsRead = true
|
||||
case "nodes.read", "users.manage":
|
||||
hadLogAccess = true
|
||||
}
|
||||
}
|
||||
if hasLogsRead || !hadLogAccess {
|
||||
continue
|
||||
}
|
||||
group.Permissions = append(group.Permissions, "logs.read")
|
||||
changed = true
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user