mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
841de2b75d
Instead of checking the value of the cookie expiration we rely on the boolean stored in the user session to check whether inactivity timeout should be disabled.
173 lines
5.2 KiB
Go
173 lines
5.2 KiB
Go
package middlewares
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
|
|
"github.com/asaskevich/govalidator"
|
|
"github.com/authelia/authelia/internal/configuration/schema"
|
|
"github.com/authelia/authelia/internal/session"
|
|
"github.com/authelia/authelia/internal/utils"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/valyala/fasthttp"
|
|
)
|
|
|
|
// NewRequestLogger create a new request logger for the given request.
|
|
func NewRequestLogger(ctx *AutheliaCtx) *logrus.Entry {
|
|
return logrus.WithFields(logrus.Fields{
|
|
"method": string(ctx.Method()),
|
|
"path": string(ctx.Path()),
|
|
"remote_ip": ctx.RemoteIP().String(),
|
|
})
|
|
}
|
|
|
|
// NewAutheliaCtx instantiate an AutheliaCtx out of a RequestCtx.
|
|
func NewAutheliaCtx(ctx *fasthttp.RequestCtx, configuration schema.Configuration, providers Providers) (*AutheliaCtx, error) {
|
|
autheliaCtx := new(AutheliaCtx)
|
|
autheliaCtx.RequestCtx = ctx
|
|
autheliaCtx.Providers = providers
|
|
autheliaCtx.Configuration = configuration
|
|
autheliaCtx.Logger = NewRequestLogger(autheliaCtx)
|
|
autheliaCtx.Clock = utils.RealClock{}
|
|
return autheliaCtx, nil
|
|
}
|
|
|
|
// AutheliaMiddleware is wrapping the RequestCtx into an AutheliaCtx providing Authelia related objects.
|
|
func AutheliaMiddleware(configuration schema.Configuration, providers Providers) func(next RequestHandler) fasthttp.RequestHandler {
|
|
return func(next RequestHandler) fasthttp.RequestHandler {
|
|
return func(ctx *fasthttp.RequestCtx) {
|
|
autheliaCtx, err := NewAutheliaCtx(ctx, configuration, providers)
|
|
if err != nil {
|
|
autheliaCtx.Error(err, operationFailedMessage)
|
|
return
|
|
}
|
|
next(autheliaCtx)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Error reply with an error and display the stack trace in the logs.
|
|
func (c *AutheliaCtx) Error(err error, message string) {
|
|
b, marshalErr := json.Marshal(ErrorResponse{Status: "KO", Message: message})
|
|
|
|
if marshalErr != nil {
|
|
c.Logger.Error(marshalErr)
|
|
}
|
|
|
|
c.SetContentType("application/json")
|
|
c.SetBody(b)
|
|
c.Logger.Error(err)
|
|
}
|
|
|
|
// ReplyError reply with an error but does not display any stack trace in the logs
|
|
func (c *AutheliaCtx) ReplyError(err error, message string) {
|
|
b, marshalErr := json.Marshal(ErrorResponse{Status: "KO", Message: message})
|
|
|
|
if marshalErr != nil {
|
|
c.Logger.Error(marshalErr)
|
|
}
|
|
|
|
c.SetContentType("application/json")
|
|
c.SetBody(b)
|
|
c.Logger.Debug(err)
|
|
}
|
|
|
|
// ReplyUnauthorized response sent when user is unauthorized
|
|
func (c *AutheliaCtx) ReplyUnauthorized() {
|
|
c.RequestCtx.Error(fasthttp.StatusMessage(fasthttp.StatusUnauthorized), fasthttp.StatusUnauthorized)
|
|
// c.Response.Header.Set("WWW-Authenticate", "Basic realm=Restricted")
|
|
}
|
|
|
|
// ReplyForbidden response sent when access is forbidden to user
|
|
func (c *AutheliaCtx) ReplyForbidden() {
|
|
c.RequestCtx.Error(fasthttp.StatusMessage(fasthttp.StatusForbidden), fasthttp.StatusForbidden)
|
|
}
|
|
|
|
// XForwardedProto return the content of the header X-Forwarded-Proto
|
|
func (c *AutheliaCtx) XForwardedProto() []byte {
|
|
return c.RequestCtx.Request.Header.Peek(xForwardedProtoHeader)
|
|
}
|
|
|
|
// XForwardedHost return the content of the header X-Forwarded-Host
|
|
func (c *AutheliaCtx) XForwardedHost() []byte {
|
|
return c.RequestCtx.Request.Header.Peek(xForwardedHostHeader)
|
|
}
|
|
|
|
// XForwardedURI return the content of the header X-Forwarded-URI
|
|
func (c *AutheliaCtx) XForwardedURI() []byte {
|
|
return c.RequestCtx.Request.Header.Peek(xForwardedURIHeader)
|
|
}
|
|
|
|
// XOriginalURL return the content of the header X-Original-URL
|
|
func (c *AutheliaCtx) XOriginalURL() []byte {
|
|
return c.RequestCtx.Request.Header.Peek(xOriginalURLHeader)
|
|
}
|
|
|
|
// GetSession return the user session. Any update will be saved in cache.
|
|
func (c *AutheliaCtx) GetSession() session.UserSession {
|
|
userSession, err := c.Providers.SessionProvider.GetSession(c.RequestCtx)
|
|
if err != nil {
|
|
c.Logger.Error("Unable to retrieve user session")
|
|
return session.NewDefaultUserSession()
|
|
}
|
|
return userSession
|
|
}
|
|
|
|
// SaveSession save the content of the session.
|
|
func (c *AutheliaCtx) SaveSession(userSession session.UserSession) error {
|
|
return c.Providers.SessionProvider.SaveSession(c.RequestCtx, userSession)
|
|
}
|
|
|
|
// ReplyOK is a helper method to reply ok
|
|
func (c *AutheliaCtx) ReplyOK() {
|
|
c.SetContentType(applicationJSONContentType)
|
|
c.SetBody(okMessageBytes)
|
|
}
|
|
|
|
// ParseBody parse the request body into the type of value
|
|
func (c *AutheliaCtx) ParseBody(value interface{}) error {
|
|
err := json.Unmarshal(c.PostBody(), &value)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to parse body: %s", err)
|
|
}
|
|
|
|
valid, err := govalidator.ValidateStruct(value)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to validate body: %s", err)
|
|
}
|
|
|
|
if !valid {
|
|
return fmt.Errorf("Body is not valid")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SetJSONBody Set json body
|
|
func (c *AutheliaCtx) SetJSONBody(value interface{}) error {
|
|
b, err := json.Marshal(OKResponse{Status: "OK", Data: value})
|
|
if err != nil {
|
|
return fmt.Errorf("Unable to marshal JSON body")
|
|
}
|
|
|
|
c.SetContentType("application/json")
|
|
c.SetBody(b)
|
|
return nil
|
|
}
|
|
|
|
// RemoteIP return the remote IP taking X-Forwarded-For header into account if provided.
|
|
func (c *AutheliaCtx) RemoteIP() net.IP {
|
|
XForwardedFor := c.Request.Header.Peek("X-Forwarded-For")
|
|
if XForwardedFor != nil {
|
|
ips := strings.Split(string(XForwardedFor), ",")
|
|
|
|
if len(ips) > 0 {
|
|
return net.ParseIP(strings.Trim(ips[0], " "))
|
|
}
|
|
}
|
|
return c.RequestCtx.RemoteIP()
|
|
}
|