mirror of https://github.com/authelia/authelia.git
279 lines
9.4 KiB
Go
279 lines
9.4 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
"strconv"
|
|
|
|
"github.com/valyala/fasthttp"
|
|
|
|
"github.com/authelia/authelia/v4/internal/middlewares"
|
|
"github.com/authelia/authelia/v4/internal/model"
|
|
"github.com/authelia/authelia/v4/internal/session"
|
|
"github.com/authelia/authelia/v4/internal/storage"
|
|
)
|
|
|
|
func getWebAuthnCredentialIDFromContext(ctx *middlewares.AutheliaCtx) (int, error) {
|
|
value := ctx.UserValue("credentialID")
|
|
|
|
switch v := value.(type) {
|
|
case nil:
|
|
return 0, fmt.Errorf("error occurred retrieving WebAuthn Credential ID from context: the user value wasn't set")
|
|
case string:
|
|
credentialID, err := strconv.Atoi(v)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("error occurred retrieving WebAuthn Credential ID from context: failed to parse '%s' as an integer: %w", v, err)
|
|
}
|
|
|
|
return credentialID, nil
|
|
default:
|
|
return 0, fmt.Errorf("error occurred retrieving WebAuthn Credential ID from context: the type '%T' is not a string", value)
|
|
}
|
|
}
|
|
|
|
// WebAuthnCredentialsGET returns all credentials registered for the current user.
|
|
func WebAuthnCredentialsGET(ctx *middlewares.AutheliaCtx) {
|
|
var (
|
|
userSession session.UserSession
|
|
origin *url.URL
|
|
err error
|
|
)
|
|
|
|
if userSession, err = ctx.GetSession(); err != nil {
|
|
ctx.Logger.WithError(err).Errorf("Error occurred loading WebAuthn credentials: %s", errStrUserSessionData)
|
|
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
|
|
|
return
|
|
}
|
|
|
|
if userSession.IsAnonymous() {
|
|
ctx.Logger.WithError(errUserAnonymous).Errorf("Error occurred loading WebAuthn credentials")
|
|
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
if origin, err = ctx.GetOrigin(); err != nil {
|
|
ctx.Logger.WithError(err).Errorf("Error occurred loading WebAuthn credentials for user '%s': error occurred attempting to retrieve origin", userSession.Username)
|
|
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
var credentials []model.WebAuthnCredential
|
|
|
|
if credentials, err = ctx.Providers.StorageProvider.LoadWebAuthnCredentialsByUsername(ctx, origin.Hostname(), userSession.Username); err != nil && err != storage.ErrNoWebAuthnCredential {
|
|
ctx.Logger.WithError(err).Errorf("Error occurred loading WebAuthn credentials for user '%s': error occurred loading credentials from the storage backend", userSession.Username)
|
|
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
if err = ctx.SetJSONBody(credentials); err != nil {
|
|
ctx.Logger.WithError(err).Errorf("Error occurred loading WebAuthn credentials for user '%s': %s", userSession.Username, errStrRespBody)
|
|
}
|
|
}
|
|
|
|
// WebAuthnCredentialPUT updates the description for a specific credential for the current user.
|
|
func WebAuthnCredentialPUT(ctx *middlewares.AutheliaCtx) {
|
|
var (
|
|
bodyJSON bodyEditWebAuthnCredentialRequest
|
|
|
|
id int
|
|
credential *model.WebAuthnCredential
|
|
userSession session.UserSession
|
|
|
|
origin *url.URL
|
|
credentials []model.WebAuthnCredential
|
|
|
|
err error
|
|
)
|
|
|
|
if userSession, err = ctx.GetSession(); err != nil {
|
|
ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential: %s", errStrUserSessionData)
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
if userSession.IsAnonymous() {
|
|
ctx.Logger.WithError(errUserAnonymous).Errorf("Error occurred modifying WebAuthn credential")
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
if err = json.Unmarshal(ctx.PostBody(), &bodyJSON); err != nil {
|
|
ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential for user '%s': %s", userSession.Username, errStrReqBodyParse)
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusBadRequest)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
if len(bodyJSON.Description) == 0 {
|
|
ctx.Logger.WithError(fmt.Errorf("description is empty")).Errorf("Error occurred modifying WebAuthn credential for user '%s", userSession.Username)
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
if id, err = getWebAuthnCredentialIDFromContext(ctx); err != nil {
|
|
ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential for user '%s': error occurred trying to determine the credential ID", userSession.Username)
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusBadRequest)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
if credential, err = ctx.Providers.StorageProvider.LoadWebAuthnCredentialByID(ctx, id); err != nil {
|
|
ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential for user '%s': error occurred loading the credential from the storage backend", userSession.Username)
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
if credential.Username != userSession.Username {
|
|
ctx.Logger.WithError(fmt.Errorf("user '%s' owns the credential with id '%d'", credential.Username, credential.ID)).Errorf("Error occurred modifying WebAuthn credential for user '%s'", userSession.Username)
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
if origin, err = ctx.GetOrigin(); err != nil {
|
|
ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential for user '%s': error occurred determining the origin for the request", userSession.Username)
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
if credentials, err = ctx.Providers.StorageProvider.LoadWebAuthnCredentialsByUsername(ctx, origin.Hostname(), userSession.Username); err != nil {
|
|
ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential for user '%s': error occurred looking up existing credentials", userSession.Username)
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
for _, c := range credentials {
|
|
if c.ID == id {
|
|
continue
|
|
}
|
|
|
|
if c.Description == bodyJSON.Description {
|
|
ctx.Logger.WithError(fmt.Errorf("credential with id '%d' also has the description '%s'", c.ID, bodyJSON.Description)).Errorf("Error occurred modifying WebAuthn credential for user '%s': error occurred ensuring the credentials had unique descriptions", userSession.Username)
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusConflict)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
}
|
|
|
|
if err = ctx.Providers.StorageProvider.UpdateWebAuthnCredentialDescription(ctx, userSession.Username, id, bodyJSON.Description); err != nil {
|
|
ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential for user '%s': error occurred while attempting to update the modified credential in the storage backend", userSession.Username)
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
ctx.ReplyOK()
|
|
}
|
|
|
|
// WebAuthnCredentialDELETE deletes a specific credential for the current user.
|
|
func WebAuthnCredentialDELETE(ctx *middlewares.AutheliaCtx) {
|
|
var (
|
|
id int
|
|
credential *model.WebAuthnCredential
|
|
userSession session.UserSession
|
|
err error
|
|
)
|
|
|
|
if userSession, err = ctx.GetSession(); err != nil {
|
|
ctx.Logger.WithError(err).Errorf("Error occurred deleting WebAuthn credential: %s", errStrUserSessionData)
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
if userSession.IsAnonymous() {
|
|
ctx.Logger.WithError(errUserAnonymous).Errorf("Error occurred modifying WebAuthn credential")
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
if id, err = getWebAuthnCredentialIDFromContext(ctx); err != nil {
|
|
ctx.Logger.WithError(err).Errorf("Error occurred deleting WebAuthn credential for user '%s': error occurred trying to determine the credential ID", userSession.Username)
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusBadRequest)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
if credential, err = ctx.Providers.StorageProvider.LoadWebAuthnCredentialByID(ctx, id); err != nil {
|
|
ctx.Logger.WithError(err).Errorf("Error occurred deleting WebAuthn credential for user '%s': error occurred trying to load the credential from the storage backend", userSession.Username)
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
if credential.Username != userSession.Username {
|
|
ctx.Logger.WithError(fmt.Errorf("user '%s' owns the credential with id '%d'", credential.Username, credential.ID)).Errorf("Error occurred deleting WebAuthn credential for user '%s'", userSession.Username)
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
if err = ctx.Providers.StorageProvider.DeleteWebAuthnCredential(ctx, credential.KID.String()); err != nil {
|
|
ctx.Logger.WithError(err).Errorf("Error occurred delete WebAuthn credential for user '%s': error occurred while attempting to delete the credential from the storage backend", userSession.Username)
|
|
|
|
ctx.SetStatusCode(fasthttp.StatusForbidden)
|
|
ctx.SetJSONError(messageOperationFailed)
|
|
|
|
return
|
|
}
|
|
|
|
body := emailEventBody{
|
|
Prefix: eventEmailAction2FAPrefix,
|
|
Body: eventEmailAction2FABody,
|
|
Suffix: eventEmailAction2FARemovedSuffix,
|
|
}
|
|
|
|
ctxLogEvent(ctx, userSession.Username, eventLogAction2FARemoved, body, map[string]any{eventLogKeyAction: eventLogAction2FARemoved, eventLogKeyCategory: eventLogCategoryWebAuthnCredential, eventLogKeyDescription: credential.Description})
|
|
|
|
ctx.ReplyOK()
|
|
}
|