fix: enforce demo no-mfa login and expose proxy uuid in session
This commit is contained in:
parent
4bbfdef8cd
commit
f1f185b6a8
52
api/api.go
52
api/api.go
@ -968,6 +968,20 @@ func (h *handler) login(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Demo/read-only account explicitly disables MFA to keep the roaming
|
||||||
|
// experience simple while write operations remain blocked by policy.
|
||||||
|
if h.isReadOnlyAccount(user) {
|
||||||
|
if user.MFAEnabled || strings.TrimSpace(user.MFATOTPSecret) != "" || !user.MFASecretIssuedAt.IsZero() || !user.MFAConfirmedAt.IsZero() {
|
||||||
|
user.MFATOTPSecret = ""
|
||||||
|
user.MFAEnabled = false
|
||||||
|
user.MFASecretIssuedAt = time.Time{}
|
||||||
|
user.MFAConfirmedAt = time.Time{}
|
||||||
|
if err := h.store.UpdateUser(c.Request.Context(), user); err != nil {
|
||||||
|
slog.Warn("failed to reset mfa state for read-only account", "err", err, "userID", user.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if user.MFAEnabled {
|
if user.MFAEnabled {
|
||||||
if totpCode == "" {
|
if totpCode == "" {
|
||||||
respondError(c, http.StatusBadRequest, "mfa_code_required", "totp code is required")
|
respondError(c, http.StatusBadRequest, "mfa_code_required", "totp code is required")
|
||||||
@ -1021,10 +1035,12 @@ func (h *handler) login(c *gin.Context) {
|
|||||||
"user": sanitizeUser(user, nil),
|
"user": sanitizeUser(user, nil),
|
||||||
}
|
}
|
||||||
|
|
||||||
if challengeToken, err := h.createMFAChallenge(user.ID); err != nil {
|
if !h.isReadOnlyAccount(user) {
|
||||||
slog.Error("failed to create mfa challenge during login", "err", err, "userID", user.ID)
|
if challengeToken, err := h.createMFAChallenge(user.ID); err != nil {
|
||||||
} else {
|
slog.Error("failed to create mfa challenge during login", "err", err, "userID", user.ID)
|
||||||
response["mfaToken"] = challengeToken
|
} else {
|
||||||
|
response["mfaToken"] = challengeToken
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, response)
|
c.JSON(http.StatusOK, response)
|
||||||
@ -2203,6 +2219,10 @@ func (h *handler) cancelSubscription(c *gin.Context) {
|
|||||||
|
|
||||||
func sanitizeUser(user *store.User, challenge *mfaChallenge) gin.H {
|
func sanitizeUser(user *store.User, challenge *mfaChallenge) gin.H {
|
||||||
identifier := strings.TrimSpace(user.ID)
|
identifier := strings.TrimSpace(user.ID)
|
||||||
|
proxyUUID := strings.TrimSpace(user.ProxyUUID)
|
||||||
|
if proxyUUID == "" {
|
||||||
|
proxyUUID = identifier
|
||||||
|
}
|
||||||
groups := user.Groups
|
groups := user.Groups
|
||||||
if len(groups) == 0 {
|
if len(groups) == 0 {
|
||||||
groups = []string{}
|
groups = []string{}
|
||||||
@ -2220,17 +2240,19 @@ func sanitizeUser(user *store.User, challenge *mfaChallenge) gin.H {
|
|||||||
permissions = cloned
|
permissions = cloned
|
||||||
}
|
}
|
||||||
return gin.H{
|
return gin.H{
|
||||||
"id": identifier,
|
"id": identifier,
|
||||||
"uuid": identifier,
|
"uuid": identifier,
|
||||||
"name": user.Name,
|
"name": user.Name,
|
||||||
"username": user.Name,
|
"username": user.Name,
|
||||||
"email": user.Email,
|
"email": user.Email,
|
||||||
"emailVerified": user.EmailVerified,
|
"emailVerified": user.EmailVerified,
|
||||||
"mfaEnabled": user.MFAEnabled,
|
"mfaEnabled": user.MFAEnabled,
|
||||||
"mfa": buildMFAState(user, challenge),
|
"mfa": buildMFAState(user, challenge),
|
||||||
"role": user.Role,
|
"role": user.Role,
|
||||||
"groups": groups,
|
"groups": groups,
|
||||||
"permissions": permissions,
|
"permissions": permissions,
|
||||||
|
"proxyUuid": proxyUUID,
|
||||||
|
"proxyUuidExpiresAt": user.ProxyUUIDExpiresAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -135,6 +135,10 @@ func ensureDemoUser(ctx context.Context, st store.Store, logger *slog.Logger) er
|
|||||||
Email: demoEmail,
|
Email: demoEmail,
|
||||||
EmailVerified: true,
|
EmailVerified: true,
|
||||||
PasswordHash: string(hashed),
|
PasswordHash: string(hashed),
|
||||||
|
MFATOTPSecret: "",
|
||||||
|
MFAEnabled: false,
|
||||||
|
MFASecretIssuedAt: time.Time{},
|
||||||
|
MFAConfirmedAt: time.Time{},
|
||||||
Level: store.LevelUser,
|
Level: store.LevelUser,
|
||||||
Role: store.RoleReadOnly,
|
Role: store.RoleReadOnly,
|
||||||
Groups: []string{demoGroup},
|
Groups: []string{demoGroup},
|
||||||
@ -156,6 +160,10 @@ func ensureDemoUser(ctx context.Context, st store.Store, logger *slog.Logger) er
|
|||||||
demoUser.Email = demoEmail
|
demoUser.Email = demoEmail
|
||||||
demoUser.EmailVerified = true
|
demoUser.EmailVerified = true
|
||||||
demoUser.PasswordHash = string(hashed)
|
demoUser.PasswordHash = string(hashed)
|
||||||
|
demoUser.MFATOTPSecret = ""
|
||||||
|
demoUser.MFAEnabled = false
|
||||||
|
demoUser.MFASecretIssuedAt = time.Time{}
|
||||||
|
demoUser.MFAConfirmedAt = time.Time{}
|
||||||
demoUser.Level = store.LevelUser
|
demoUser.Level = store.LevelUser
|
||||||
demoUser.Role = store.RoleReadOnly
|
demoUser.Role = store.RoleReadOnly
|
||||||
demoUser.Groups = []string{demoGroup}
|
demoUser.Groups = []string{demoGroup}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user