diff options
Diffstat (limited to 'internal/middlewares/require_auth.go')
| -rw-r--r-- | internal/middlewares/require_auth.go | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/internal/middlewares/require_auth.go b/internal/middlewares/require_auth.go new file mode 100644 index 000000000..3ae12db36 --- /dev/null +++ b/internal/middlewares/require_auth.go @@ -0,0 +1,126 @@ +package middlewares + +import ( + "github.com/valyala/fasthttp" + + "github.com/authelia/authelia/v4/internal/authentication" + "github.com/authelia/authelia/v4/internal/model" + "github.com/authelia/authelia/v4/internal/session" +) + +// Require1FA check if user has enough permissions to execute the next handler. +func Require1FA(next RequestHandler) RequestHandler { + return func(ctx *AutheliaCtx) { + if s, err := ctx.GetSession(); err != nil || s.AuthenticationLevel < authentication.OneFactor { + ctx.ReplyForbidden() + return + } + + next(ctx) + } +} + +type ElevatedForbiddenResponse struct { + Elevation bool `json:"elevation"` + FirstFactor bool `json:"first_factor"` + SecondFactor bool `json:"second_factor"` +} + +func RequireElevated(next RequestHandler) RequestHandler { + return func(ctx *AutheliaCtx) { + var ( + userSession session.UserSession + err error + ) + + if userSession, err = ctx.GetSession(); err != nil { + ctx.Logger.WithError(err).Error("Error occurred attempting to lookup user session during an elevation check.") + + if err = ctx.ReplyJSON(OKResponse{Status: "KO", Data: ElevatedForbiddenResponse{FirstFactor: true}}, fasthttp.StatusForbidden); err != nil { + ctx.Logger.WithError(err).Error("Error occurred encoding JSON response during an elevation check.") + } + + return + } + + if userSession.AuthenticationLevel < authentication.OneFactor { + ctx.Logger.Warn("An anonymous user attempted to access an elevated protected endpoint.") + + if err = ctx.ReplyJSON(OKResponse{Status: "KO", Data: ElevatedForbiddenResponse{FirstFactor: true}}, fasthttp.StatusForbidden); err != nil { + ctx.Logger.WithError(err).Error("Error occurred encoding JSON response during an elevation check.") + } + + return + } + + if ctx.Configuration.IdentityValidation.ElevatedSession.SkipSecondFactor && userSession.AuthenticationLevel >= authentication.TwoFactor { + ctx.Logger.WithFields(map[string]any{"user": userSession.Username}).Trace("The user session elevation was not checked as the user has performed second factor authentication and the policy to skip this is enabled.") + + next(ctx) + + return + } + + if ctx.Configuration.IdentityValidation.ElevatedSession.RequireSecondFactor && userSession.AuthenticationLevel < authentication.TwoFactor { + var info model.UserInfo + + if info, err = ctx.Providers.StorageProvider.LoadUserInfo(ctx, userSession.Username); err != nil { + ctx.Logger.WithError(err).Error("Error occurred attempting to lookup user information during a elevation check.") + + if err = ctx.ReplyJSON(OKResponse{Status: "KO", Data: ElevatedForbiddenResponse{SecondFactor: true}}, fasthttp.StatusForbidden); err != nil { + ctx.Logger.WithError(err).Error("Error occurred encoding JSON response during an elevation check.") + } + + return + } + + if info.HasTOTP || info.HasWebAuthn || info.HasDuo { + ctx.Logger.WithFields(map[string]any{"user": userSession.Username}).Info("The user session elevation was not checked as the user must have also performed second factor authentication.") + + if err = ctx.ReplyJSON(OKResponse{Status: "KO", Data: ElevatedForbiddenResponse{SecondFactor: true}}, fasthttp.StatusForbidden); err != nil { + ctx.Logger.WithError(err).Error("Error occurred encoding JSON response during an elevation check.") + } + + return + } + } + + if userSession.Elevations.User == nil { + if err = ctx.ReplyJSON(OKResponse{Status: "KO", Data: ElevatedForbiddenResponse{Elevation: true}}, fasthttp.StatusForbidden); err != nil { + ctx.Logger.WithError(err).Error("Error occurred encoding JSON response during an elevation check.") + } + + return + } + + invalid := false + + if ctx.GetClock().Now().After(userSession.Elevations.User.Expires) { + invalid = true + + ctx.Logger.WithFields(map[string]any{"user": userSession.Username, "expired": userSession.Elevations.User.Expires.Unix()}).Info("The user session elevation was expired. It will be destroyed and the users access will be forbidden.") + } + + if !ctx.RemoteIP().Equal(userSession.Elevations.User.RemoteIP) { + invalid = true + + ctx.Logger.WithFields(map[string]any{"user": userSession.Username, "expected_ip": userSession.Elevations.User.RemoteIP.String()}).Warn("The user session elevation did not have a matching IP. It will be destroyed and the users access will be forbidden.") + } + + if invalid { + userSession.Elevations.User = nil + + if err = ctx.SaveSession(userSession); err != nil { + ctx.Logger.WithError(err).Error("Error occurred trying to save the user session after a policy constraint violation occurred.") + } + + if err = ctx.ReplyJSON(OKResponse{Status: "KO", Data: ElevatedForbiddenResponse{Elevation: true}}, fasthttp.StatusForbidden); err != nil { + ctx.Logger.WithError(err).Error("Error occurred encoding JSON response during an elevation check.") + } + + return + } + + next(ctx) + } +} |
