authelia/internal/middlewares/authelia_context.go
Clement Michaud 841de2b75d Disable inactivity timeout when user checked remember me.
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.
2020-01-18 00:12:36 +01:00

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()
}