Changeset 0.10.0 (#56)
This commit is contained in:
@@ -20,12 +20,13 @@ import (
|
||||
)
|
||||
|
||||
type AdminHandler struct {
|
||||
stores store.Stores
|
||||
vault *crypto.KeyResolver
|
||||
stores store.Stores
|
||||
vault *crypto.KeyResolver
|
||||
uekCache *crypto.UEKCache
|
||||
}
|
||||
|
||||
func NewAdminHandler(s store.Stores, vault *crypto.KeyResolver) *AdminHandler {
|
||||
return &AdminHandler{stores: s, vault: vault}
|
||||
func NewAdminHandler(s store.Stores, vault *crypto.KeyResolver, uekCache *crypto.UEKCache) *AdminHandler {
|
||||
return &AdminHandler{stores: s, vault: vault, uekCache: uekCache}
|
||||
}
|
||||
|
||||
// ── User Management ─────────────────────────
|
||||
@@ -145,10 +146,50 @@ func (h *AdminHandler) ResetPassword(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Destroy vault — old UEK is unrecoverable with new password.
|
||||
// A fresh vault will be initialized on the user's next login.
|
||||
h.destroyVault(c, id)
|
||||
|
||||
h.auditLog(c, "user.password_reset", "user", id, nil)
|
||||
c.JSON(http.StatusOK, gin.H{"message": "password reset"})
|
||||
}
|
||||
|
||||
// destroyVault nullifies a user's encrypted UEK and deletes their personal
|
||||
// provider keys. Called when an admin resets a user's password — the old
|
||||
// UEK cannot be recovered without the old password.
|
||||
func (h *AdminHandler) destroyVault(c *gin.Context, userID string) {
|
||||
if h.uekCache == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Null out vault columns — user gets a fresh vault on next login
|
||||
_, err := database.DB.Exec(`
|
||||
UPDATE users
|
||||
SET encrypted_uek = NULL, uek_salt = NULL, uek_nonce = NULL, vault_set = false
|
||||
WHERE id = $1
|
||||
`, userID)
|
||||
if err != nil {
|
||||
log.Printf("⚠ Failed to clear vault for user %s: %v", userID, err)
|
||||
}
|
||||
|
||||
// Evict from session cache (if user is currently logged in)
|
||||
h.uekCache.Evict(userID)
|
||||
|
||||
// Delete personal provider configs — their keys are undecryptable now
|
||||
result, err := database.DB.Exec(`
|
||||
DELETE FROM provider_configs WHERE scope = 'personal' AND owner_id = $1
|
||||
`, userID)
|
||||
if err != nil {
|
||||
log.Printf("⚠ Failed to delete personal providers for user %s: %v", userID, err)
|
||||
} else if rows, _ := result.RowsAffected(); rows > 0 {
|
||||
log.Printf(" 🔐 Destroyed %d personal provider(s) for user %s", rows, userID)
|
||||
}
|
||||
|
||||
h.auditLog(c, "user.vault_destroyed", "user", userID, models.JSONMap{
|
||||
"reason": "admin_password_reset",
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) DeleteUser(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if err := h.stores.Users.Delete(c.Request.Context(), id); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user