2023-01-25 09:36:40 +00:00
package handlers
import (
"fmt"
"net/url"
"github.com/authelia/authelia/v4/internal/authentication"
"github.com/authelia/authelia/v4/internal/authorization"
"github.com/authelia/authelia/v4/internal/middlewares"
"github.com/authelia/authelia/v4/internal/session"
"github.com/authelia/authelia/v4/internal/utils"
)
// Handler is the middlewares.RequestHandler for Authz.
func ( authz * Authz ) Handler ( ctx * middlewares . AutheliaCtx ) {
var (
object authorization . Object
autheliaURL * url . URL
provider * session . Session
err error
)
if object , err = authz . handleGetObject ( ctx ) ; err != nil {
2023-04-08 04:48:55 +00:00
ctx . Logger . WithError ( err ) . Error ( "Error getting Target URL and Request Method" )
2023-01-25 09:36:40 +00:00
2023-04-08 04:48:55 +00:00
ctx . ReplyStatusCode ( authz . config . StatusCodeBadRequest )
2023-01-25 09:36:40 +00:00
return
}
if ! utils . IsURISecure ( object . URL ) {
ctx . Logger . Errorf ( "Target URL '%s' has an insecure scheme '%s', only the 'https' and 'wss' schemes are supported so session cookies can be transmitted securely" , object . URL . String ( ) , object . URL . Scheme )
2023-04-08 04:48:55 +00:00
ctx . ReplyStatusCode ( authz . config . StatusCodeBadRequest )
2023-01-25 09:36:40 +00:00
return
}
if provider , err = ctx . GetSessionProviderByTargetURL ( object . URL ) ; err != nil {
2023-04-08 04:48:55 +00:00
ctx . Logger . WithError ( err ) . WithField ( "target_url" , object . URL . String ( ) ) . Error ( "Target URL does not appear to have a relevant session cookies configuration" )
2023-01-25 09:36:40 +00:00
2023-04-08 04:48:55 +00:00
ctx . ReplyStatusCode ( authz . config . StatusCodeBadRequest )
2023-01-25 09:36:40 +00:00
return
}
if autheliaURL , err = authz . getAutheliaURL ( ctx , provider ) ; err != nil {
2023-04-08 04:48:55 +00:00
ctx . Logger . WithError ( err ) . WithField ( "target_url" , object . URL . String ( ) ) . Error ( "Error occurred trying to determine the external Authelia URL for Target URL" )
2023-01-25 09:36:40 +00:00
2023-04-08 04:48:55 +00:00
ctx . ReplyStatusCode ( authz . config . StatusCodeBadRequest )
2023-01-25 09:36:40 +00:00
return
}
var (
authn Authn
strategy AuthnStrategy
)
if authn , strategy , err = authz . authn ( ctx , provider ) ; err != nil {
authn . Object = object
ctx . Logger . WithError ( err ) . Error ( "Error occurred while attempting to authenticate a request" )
switch strategy {
case nil :
ctx . ReplyUnauthorized ( )
default :
strategy . HandleUnauthorized ( ctx , & authn , authz . getRedirectionURL ( & object , autheliaURL ) )
}
return
}
authn . Object = object
authn . Method = friendlyMethod ( authn . Object . Method )
ruleHasSubject , required := ctx . Providers . Authorizer . GetRequiredLevel (
authorization . Subject {
Username : authn . Details . Username ,
Groups : authn . Details . Groups ,
IP : ctx . RemoteIP ( ) ,
} ,
object ,
)
switch isAuthzResult ( authn . Level , required , ruleHasSubject ) {
case AuthzResultForbidden :
ctx . Logger . Infof ( "Access to '%s' is forbidden to user '%s'" , object . URL . String ( ) , authn . Username )
ctx . ReplyForbidden ( )
case AuthzResultUnauthorized :
var handler HandlerAuthzUnauthorized
if strategy != nil {
handler = strategy . HandleUnauthorized
} else {
handler = authz . handleUnauthorized
}
handler ( ctx , & authn , authz . getRedirectionURL ( & object , autheliaURL ) )
case AuthzResultAuthorized :
authz . handleAuthorized ( ctx , & authn )
}
}
func ( authz * Authz ) getAutheliaURL ( ctx * middlewares . AutheliaCtx , provider * session . Session ) ( autheliaURL * url . URL , err error ) {
if autheliaURL , err = authz . handleGetAutheliaURL ( ctx ) ; err != nil {
return nil , err
}
2023-04-08 04:48:55 +00:00
switch {
case authz . implementation == AuthzImplLegacy :
2023-01-25 09:36:40 +00:00
return autheliaURL , nil
2023-04-08 04:48:55 +00:00
case autheliaURL != nil :
switch {
case utils . HasURIDomainSuffix ( autheliaURL , provider . Config . Domain ) :
return autheliaURL , nil
default :
return nil , fmt . Errorf ( "authelia url '%s' is not valid for detected domain '%s' as the url does not have the domain as a suffix" , autheliaURL . String ( ) , provider . Config . Domain )
}
2023-01-25 09:36:40 +00:00
}
if provider . Config . AutheliaURL != nil {
return provider . Config . AutheliaURL , nil
}
return nil , fmt . Errorf ( "authelia url lookup failed" )
}
func ( authz * Authz ) getRedirectionURL ( object * authorization . Object , autheliaURL * url . URL ) ( redirectionURL * url . URL ) {
if autheliaURL == nil {
return nil
}
redirectionURL , _ = url . ParseRequestURI ( autheliaURL . String ( ) )
2023-04-08 04:48:55 +00:00
if redirectionURL . Path == "" {
redirectionURL . Path = "/"
}
2023-01-25 09:36:40 +00:00
qry := redirectionURL . Query ( )
qry . Set ( queryArgRD , object . URL . String ( ) )
if object . Method != "" {
qry . Set ( queryArgRM , object . Method )
}
redirectionURL . RawQuery = qry . Encode ( )
return redirectionURL
}
func ( authz * Authz ) authn ( ctx * middlewares . AutheliaCtx , provider * session . Session ) ( authn Authn , strategy AuthnStrategy , err error ) {
for _ , strategy = range authz . strategies {
if authn , err = strategy . Get ( ctx , provider ) ; err != nil {
if strategy . CanHandleUnauthorized ( ) {
2023-04-08 04:48:55 +00:00
return Authn { Type : authn . Type , Level : authentication . NotAuthenticated , Username : anonymous } , strategy , err
2023-01-25 09:36:40 +00:00
}
2023-04-08 04:48:55 +00:00
return Authn { Type : authn . Type , Level : authentication . NotAuthenticated , Username : anonymous } , nil , err
2023-01-25 09:36:40 +00:00
}
if authn . Level != authentication . NotAuthenticated {
break
}
}
if strategy . CanHandleUnauthorized ( ) {
return authn , strategy , err
}
return authn , nil , nil
}