authelia/internal/middlewares/require_auth.go

136 lines
5.0 KiB
Go

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)
}
}
// RequireElevated requires various elevation criteria.
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 !handleRequireElevatedShouldDoNext(ctx, &userSession) {
return
}
next(ctx)
}
}
func handleRequireElevatedShouldDoNext(ctx *AutheliaCtx, userSession *session.UserSession) (doNext bool) {
var err error
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.")
return true
}
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
}
return handleRequireElevatedShouldDoNextValidate(ctx, userSession)
}
func handleRequireElevatedShouldDoNextValidate(ctx *AutheliaCtx, userSession *session.UserSession) (doNext bool) {
var err error
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
}
return true
}