-
Notifications
You must be signed in to change notification settings - Fork 191
sso_proxy: reduce direct calls to ValidateGroup() and clean up logic #275
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 4 commits
b27f1f8
0ae7ffd
8e49702
6b9497e
fcef7d3
461036b
54d03fb
1b7d6d3
d69ebed
1e8a5f4
161e37c
5684a89
5041e5a
b13dfde
2ab3891
e27db33
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -359,7 +359,7 @@ func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, req *http.Request, code i | |
| p.templates.ExecuteTemplate(rw, "error.html", t) | ||
| } | ||
|
|
||
| // IsWhitelistedRequest cheks that proxy host exists and checks the SkipAuthRegex | ||
| // IsWhitelistedRequest checks that proxy host exists and checks the SkipAuthRegex | ||
| func (p *OAuthProxy) IsWhitelistedRequest(req *http.Request) bool { | ||
| if p.skipAuthPreflight && req.Method == "OPTIONS" { | ||
| return true | ||
|
|
@@ -375,6 +375,27 @@ func (p *OAuthProxy) IsWhitelistedRequest(req *http.Request) bool { | |
| return false | ||
| } | ||
|
|
||
| // runValidatorsWithGracePeriod runs all validators and upon finding errors, checks to see if the | ||
| // auth provider is explicity denying authentication or if it's merely unavailable. If it's unavailable, | ||
| // we check whether the session is within the grace period or not to determine the specific error we return. | ||
| func (p *OAuthProxy) runValidatorsWithGracePeriod(session *sessions.SessionState) (err error) { | ||
jphines marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| logger := log.NewLogEntry() | ||
| errors := options.RunValidators(p.Validators, session) | ||
| if len(errors) == len(p.Validators) { | ||
jphines marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| for _, err := range errors { | ||
| // Check to see if the auth provider is explicity denying authentication, or if it is merely unavailable. | ||
| if err == providers.ErrAuthProviderUnavailable && session.IsWithinGracePeriod(p.provider.Data().GracePeriodTTL) { | ||
| return err | ||
| } | ||
| } | ||
| allowedGroups := p.upstreamConfig.AllowedGroups | ||
|
||
| logger.WithUser(session.Email).WithAllowedGroups(allowedGroups).Error(errors, | ||
| "no longer authorized after validation period") | ||
| return ErrUserNotAuthorized | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func (p *OAuthProxy) isXHR(req *http.Request) bool { | ||
| return req.Header.Get("X-Requested-With") == "XMLHttpRequest" | ||
| } | ||
|
|
@@ -693,8 +714,6 @@ func (p *OAuthProxy) Authenticate(rw http.ResponseWriter, req *http.Request) (er | |
| remoteAddr := getRemoteAddr(req) | ||
| tags := []string{"action:authenticate"} | ||
|
|
||
| allowedGroups := p.upstreamConfig.AllowedGroups | ||
|
|
||
| // Clear the session cookie if anything goes wrong. | ||
| defer func() { | ||
| if err != nil { | ||
|
|
@@ -705,7 +724,7 @@ func (p *OAuthProxy) Authenticate(rw http.ResponseWriter, req *http.Request) (er | |
| session, err := p.sessionStore.LoadSession(req) | ||
| if err != nil { | ||
| // We loaded a cookie but it wasn't valid, clear it, and reject the request | ||
| logger.Error(err, "error authenticating user") | ||
| logger.Error(err, "invalid session loaded") | ||
| return err | ||
| } | ||
|
|
||
|
|
@@ -728,14 +747,17 @@ func (p *OAuthProxy) Authenticate(rw http.ResponseWriter, req *http.Request) (er | |
| } else if session.RefreshPeriodExpired() { | ||
| // Refresh period is the period in which the access token is valid. This is ultimately | ||
| // controlled by the upstream provider and tends to be around 1 hour. | ||
| ok, err := p.provider.RefreshSession(session, allowedGroups) | ||
| // If it has expired we: | ||
| // - attempt to refresh the session | ||
| // - run email domain, email address, and email group validations against the session (if defined). | ||
|
|
||
| ok, err := p.provider.RefreshSession(session) | ||
| // We failed to refresh the session successfully | ||
| // clear the cookie and reject the request | ||
| if err != nil { | ||
| logger.WithUser(session.Email).Error(err, "refreshing session failed") | ||
| return err | ||
| } | ||
|
|
||
| if !ok { | ||
| // User is not authorized after refresh | ||
| // clear the cookie and reject the request | ||
|
|
@@ -744,6 +766,18 @@ func (p *OAuthProxy) Authenticate(rw http.ResponseWriter, req *http.Request) (er | |
| return ErrUserNotAuthorized | ||
| } | ||
|
|
||
| err = p.runValidatorsWithGracePeriod(session) | ||
| if err != nil { | ||
| switch err { | ||
| case providers.ErrAuthProviderUnavailable: | ||
| tags = append(tags, "action:refresh_session", "error:validation_failed") | ||
| p.StatsdClient.Incr("provider_error_fallback", tags, 1.0) | ||
| session.RefreshDeadline = sessions.ExtendDeadline(p.provider.Data().SessionValidTTL) | ||
| default: | ||
| return ErrUserNotAuthorized | ||
| } | ||
| } | ||
|
|
||
| err = p.sessionStore.SaveSession(rw, req, session) | ||
| if err != nil { | ||
| // We refreshed the session successfully, but failed to save it. | ||
|
|
@@ -757,9 +791,11 @@ func (p *OAuthProxy) Authenticate(rw http.ResponseWriter, req *http.Request) (er | |
| } else if session.ValidationPeriodExpired() { | ||
| // Validation period has expired, this is the shortest interval we use to | ||
| // check for valid requests. This should be set to something like a minute. | ||
| // This calls up the provider chain to validate this user is still active | ||
| // and hasn't been de-authorized. | ||
| ok := p.provider.ValidateSessionState(session, allowedGroups) | ||
| // In this case we: | ||
| // - call up the provider chain to validate this user is still active and hasn't been de-authorized. | ||
| // - run any defined email domain, email address, and email group validators against the session | ||
|
|
||
| ok := p.provider.ValidateSessionToken(session) | ||
Jusshersmith marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if !ok { | ||
| // This user is now no longer authorized, or we failed to | ||
| // validate the user. | ||
|
|
@@ -769,6 +805,18 @@ func (p *OAuthProxy) Authenticate(rw http.ResponseWriter, req *http.Request) (er | |
| return ErrUserNotAuthorized | ||
| } | ||
|
|
||
| err = p.runValidatorsWithGracePeriod(session) | ||
| if err != nil { | ||
| switch err { | ||
| case providers.ErrAuthProviderUnavailable: | ||
| tags = append(tags, "action:validate_session", "error:validation_failed") | ||
| p.StatsdClient.Incr("provider_error_fallback", tags, 1.0) | ||
| session.ValidDeadline = sessions.ExtendDeadline(p.provider.Data().SessionValidTTL) | ||
| default: | ||
| return ErrUserNotAuthorized | ||
| } | ||
| } | ||
|
|
||
| err = p.sessionStore.SaveSession(rw, req, session) | ||
| if err != nil { | ||
| // We validated the session successfully, but failed to save it. | ||
|
|
@@ -781,25 +829,6 @@ func (p *OAuthProxy) Authenticate(rw http.ResponseWriter, req *http.Request) (er | |
| } | ||
| } | ||
|
|
||
| // We revalidate group membership whenever the session is refreshed or revalidated | ||
jphines marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // just above in the call to ValidateSessionState and RefreshSession. | ||
| // To reduce strain on upstream identity providers we only revalidate email domains and | ||
| // addresses on each request here. | ||
| for _, v := range p.Validators { | ||
| _, EmailGroupValidator := v.(options.EmailGroupValidator) | ||
|
|
||
| if !EmailGroupValidator { | ||
| err := v.Validate(session) | ||
| if err != nil { | ||
| tags = append(tags, "error:validation_failed") | ||
| p.StatsdClient.Incr("application_error", tags, 1.0) | ||
| logger.WithRemoteAddress(remoteAddr).WithUser(session.Email).Info( | ||
| fmt.Sprintf("permission denied: unauthorized: %q", err)) | ||
| return ErrUserNotAuthorized | ||
| } | ||
| } | ||
| } | ||
|
|
||
| logger.WithRemoteAddress(remoteAddr).WithUser(session.Email).Info( | ||
| fmt.Sprintf("authentication: user validated")) | ||
|
|
||
|
|
||

Uh oh!
There was an error while loading. Please reload this page.