feat: denied

Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com>
feat-oidc-policies
James Elliott 2023-06-19 10:56:02 +10:00
parent 2f9da2b7e0
commit f290fd90b1
No known key found for this signature in database
GPG Key ID: 0F1C4A096E857E49
6 changed files with 53 additions and 33 deletions

View File

@ -66,8 +66,8 @@ func validateOIDCPolicies(config *schema.OpenIDConnect, val *schema.StructValida
switch name { switch name {
case "": case "":
val.Push(fmt.Errorf("policy must have a name")) val.Push(fmt.Errorf("policy must have a name"))
case policyOneFactor, policyTwoFactor: case policyOneFactor, policyTwoFactor, policyDeny:
val.Push(fmt.Errorf("policy names can't be named one_factor or two_factor")) val.Push(fmt.Errorf("policy names can't be named any of %s", strJoinAnd([]string{policyOneFactor, policyTwoFactor, policyDeny})))
default: default:
break break
} }
@ -75,10 +75,10 @@ func validateOIDCPolicies(config *schema.OpenIDConnect, val *schema.StructValida
switch policy.DefaultPolicy { switch policy.DefaultPolicy {
case "": case "":
policy.DefaultPolicy = policyTwoFactor policy.DefaultPolicy = policyTwoFactor
case policyOneFactor, policyTwoFactor: case policyOneFactor, policyTwoFactor, policyDeny:
break break
default: default:
val.Push(fmt.Errorf("policy must be one of one_factor or two_factor")) val.Push(fmt.Errorf("policy must be one of %s", strJoinAnd([]string{policyOneFactor, policyTwoFactor, policyDeny})))
} }
if len(policy.Rules) == 0 { if len(policy.Rules) == 0 {
@ -89,10 +89,10 @@ func validateOIDCPolicies(config *schema.OpenIDConnect, val *schema.StructValida
switch rule.Policy { switch rule.Policy {
case "": case "":
policy.Rules[i].Policy = policyTwoFactor policy.Rules[i].Policy = policyTwoFactor
case policyOneFactor, policyTwoFactor: case policyOneFactor, policyTwoFactor, policyDeny:
break break
default: default:
val.Push(fmt.Errorf("policy must be one of one_factor or two_factor")) val.Push(fmt.Errorf("policy must be one of %s", strJoinAnd([]string{policyOneFactor, policyTwoFactor, policyDeny})))
} }
if len(rule.Subjects) == 0 { if len(rule.Subjects) == 0 {

View File

@ -6,7 +6,6 @@ import (
"net/url" "net/url"
"time" "time"
"github.com/authelia/authelia/v4/internal/authorization"
"github.com/ory/fosite" "github.com/ory/fosite"
"github.com/authelia/authelia/v4/internal/authorization" "github.com/authelia/authelia/v4/internal/authorization"

View File

@ -8,7 +8,6 @@ import (
"path" "path"
"strings" "strings"
"github.com/authelia/authelia/v4/internal/authorization"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/ory/fosite" "github.com/ory/fosite"
@ -30,10 +29,12 @@ func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.UR
var handler handlerAuthorizationConsent var handler handlerAuthorizationConsent
policy := client.GetAuthorizationPolicy(authorization.Subject{Username: userSession.Username, Groups: userSession.Groups, IP: ctx.RemoteIP()})
switch { switch {
case userSession.IsAnonymous(): case userSession.IsAnonymous():
handler = handleOIDCAuthorizationConsentNotAuthenticated handler = handleOIDCAuthorizationConsentNotAuthenticated
case client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel, authorization.Subject{Username: userSession.Username, Groups: userSession.Groups, IP: ctx.RemoteIP()}): case authorization.IsAuthLevelSufficient(userSession.AuthenticationLevel, policy):
if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil { if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil {
ctx.Logger.Errorf(logFmtErrConsentCantGetSubject, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.Username, client.GetSectorIdentifier(), err) ctx.Logger.Errorf(logFmtErrConsentCantGetSubject, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.Username, client.GetSectorIdentifier(), err)
@ -57,6 +58,14 @@ func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, issuer *url.UR
return nil, true return nil, true
} }
default: default:
if policy == authorization.Denied {
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: the user '%s' is not authorized to use this client", requester.GetID(), client.GetID(), userSession.Username)
ctx.Providers.OpenIDConnect.WriteAuthorizeError(ctx, rw, requester, oidc.ErrClientAuthorizationUserAccessDenied)
return nil, true
}
if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil { if subject, err = ctx.Providers.OpenIDConnect.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil {
ctx.Logger.Errorf(logFmtErrConsentCantGetSubject, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.Username, client.GetSectorIdentifier(), err) ctx.Logger.Errorf(logFmtErrConsentCantGetSubject, requester.GetID(), client.GetID(), client.GetConsentPolicy(), userSession.Username, client.GetSectorIdentifier(), err)

View File

@ -8,7 +8,6 @@ import (
"path" "path"
"time" "time"
"github.com/authelia/authelia/v4/internal/authorization"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/authelia/authelia/v4/internal/authorization" "github.com/authelia/authelia/v4/internal/authorization"
@ -88,6 +87,14 @@ func OpenIDConnectConsentPOST(ctx *middlewares.AutheliaCtx) {
return return
} }
if !client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel, authorization.Subject{Username: userSession.Username, Groups: userSession.Groups, IP: ctx.RemoteIP()}) {
ctx.Logger.Errorf("User '%s' can't consent to authorization request for client with id '%s' as they are not sufficiently authenticated",
userSession.Username, consent.ClientID)
ctx.SetJSONError(messageOperationFailed)
return
}
if bodyJSON.Consent { if bodyJSON.Consent {
consent.Grant() consent.Grant()

View File

@ -221,34 +221,37 @@ func handleOIDCWorkflowResponseWithID(ctx *middlewares.AutheliaCtx, id string) {
return return
} }
if !client.IsAuthenticationLevelSufficient(userSession.AuthenticationLevel, authorization.Subject{Username: userSession.Username, Groups: userSession.Groups, IP: ctx.RemoteIP()}) { policy := client.GetAuthorizationPolicy(authorization.Subject{Username: userSession.Username, Groups: userSession.Groups, IP: ctx.RemoteIP()})
switch {
case authorization.IsAuthLevelSufficient(userSession.AuthenticationLevel, policy), policy == authorization.Denied:
var (
targetURL *url.URL
form url.Values
)
targetURL = ctx.RootURL()
if form, err = consent.GetForm(); err != nil {
ctx.Error(fmt.Errorf("unable to get authorization form values from consent session with challenge id '%s': %w", consent.ChallengeID, err), messageAuthenticationFailed)
return
}
form.Set(queryArgConsentID, workflowID.String())
targetURL.Path = path.Join(targetURL.Path, oidc.EndpointPathAuthorization)
targetURL.RawQuery = form.Encode()
if err = ctx.SetJSONBody(redirectResponse{Redirect: targetURL.String()}); err != nil {
ctx.Logger.Errorf("Unable to set default redirection URL in body: %s", err)
}
default:
ctx.Logger.Warnf("OpenID Connect client '%s' requires 2FA, cannot be redirected yet", client.GetID()) ctx.Logger.Warnf("OpenID Connect client '%s' requires 2FA, cannot be redirected yet", client.GetID())
ctx.ReplyOK() ctx.ReplyOK()
return return
} }
var (
targetURL *url.URL
form url.Values
)
targetURL = ctx.RootURL()
if form, err = consent.GetForm(); err != nil {
ctx.Error(fmt.Errorf("unable to get authorization form values from consent session with challenge id '%s': %w", consent.ChallengeID, err), messageAuthenticationFailed)
return
}
form.Set(queryArgConsentID, workflowID.String())
targetURL.Path = path.Join(targetURL.Path, oidc.EndpointPathAuthorization)
targetURL.RawQuery = form.Encode()
if err = ctx.SetJSONBody(redirectResponse{Redirect: targetURL.String()}); err != nil {
ctx.Logger.Errorf("Unable to set default redirection URL in body: %s", err)
}
} }
func markAuthenticationAttempt(ctx *middlewares.AutheliaCtx, successful bool, bannedUntil *time.Time, username string, authType string, errAuth error) (err error) { func markAuthenticationAttempt(ctx *middlewares.AutheliaCtx, successful bool, bannedUntil *time.Time, username string, authType string, errAuth error) (err error) {

View File

@ -32,4 +32,6 @@ var (
// ErrPAREnforcedClientMissingPAR is sent when a client has EnforcePAR configured but the Authorization Request was not Pushed. // ErrPAREnforcedClientMissingPAR is sent when a client has EnforcePAR configured but the Authorization Request was not Pushed.
ErrPAREnforcedClientMissingPAR = fosite.ErrInvalidRequest.WithHint("Pushed Authorization Requests are enforced for this client but no such request was sent.") ErrPAREnforcedClientMissingPAR = fosite.ErrInvalidRequest.WithHint("Pushed Authorization Requests are enforced for this client but no such request was sent.")
ErrClientAuthorizationUserAccessDenied = fosite.ErrAccessDenied.WithHint("The user was denied access to this client.")
) )