diff --git a/CHANGELOG.md b/CHANGELOG.md index 831cd1909a..698141f7f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ If you are using an architecture specific tag (ex: v7.2.1-arm64) you should move ## Changes since v7.2.1 +- [#1595](https://github.com/oauth2-proxy/oauth2-proxy/pull/1595) Add optional `allowed_emails` query parameter to the `auth_request`. (@zv0n) - [#1478](https://github.com/oauth2-proxy/oauth2-proxy/pull/1478) Parameterise the runtime image (@omBratteng) - [#1583](https://github.com/oauth2-proxy/oauth2-proxy/pull/1583) Add groups to session too when creating session from bearer token (@adriananeci) - [#1418](https://github.com/oauth2-proxy/oauth2-proxy/pull/1418) Support for passing arbitrary query parameters through from `/oauth2/start` to the identity provider's login URL. Configuration settings control which parameters are passed by default and precisely which values can be overridden per-request (@ianroberts) diff --git a/docs/docs/features/endpoints.md b/docs/docs/features/endpoints.md index 4832a5de18..61e1107b73 100644 --- a/docs/docs/features/endpoints.md +++ b/docs/docs/features/endpoints.md @@ -41,4 +41,5 @@ This endpoint returns 202 Accepted response or a 401 Unauthorized response. It can be configured using the following query parameters query parameters: - `allowed_groups`: comma separated list of allowed groups -- `allowed_email_domains`: comma separated list of allowed email domains \ No newline at end of file +- `allowed_email_domains`: comma separated list of allowed email domains +- `allowed_emails`: comma separated list of allowed emails \ No newline at end of file diff --git a/oauthproxy.go b/oauthproxy.go index 308f806b44..a571fcab1d 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -1021,6 +1021,7 @@ func authOnlyAuthorize(req *http.Request, s *sessionsapi.SessionState) bool { constraints := []func(*http.Request, *sessionsapi.SessionState) bool{ checkAllowedGroups, checkAllowedEmailDomains, + checkAllowedEmails, } for _, constraint := range constraints { @@ -1091,6 +1092,26 @@ func checkAllowedGroups(req *http.Request, s *sessionsapi.SessionState) bool { return false } +// checkAllowedEmails allow email restrictions based on the `allowed_emails` +// querystring parameter +func checkAllowedEmails(req *http.Request, s *sessionsapi.SessionState) bool { + allowedEmails := extractAllowedEntities(req, "allowed_emails") + if len(allowedEmails) == 0 { + return true + } + + allowed := false + + for email := range allowedEmails { + if email == s.Email { + allowed = true + break + } + } + + return allowed +} + // encodedState builds the OAuth state param out of our nonce and // original application redirect func encodeState(nonce string, redirect string) string { diff --git a/oauthproxy_test.go b/oauthproxy_test.go index 90b27d592f..25d23b450d 100644 --- a/oauthproxy_test.go +++ b/oauthproxy_test.go @@ -2725,7 +2725,7 @@ func TestAuthOnlyAllowedEmailDomains(t *testing.T) { expectedStatusCode: http.StatusForbidden, }, { - name: "UserInAllowedEmailDomains", + name: "UserNotInAllowedEmailDomains", email: "toto@example.com", querystring: "?allowed_email_domains=a.example.com,b.example.com", expectedStatusCode: http.StatusForbidden, @@ -2789,3 +2789,70 @@ func TestAuthOnlyAllowedEmailDomains(t *testing.T) { }) } } + +func TestAuthOnlyAllowedEmails(t *testing.T) { + testCases := []struct { + name string + email string + querystring string + expectedStatusCode int + }{ + { + name: "NotEmailRestriction", + email: "toto@example.com", + querystring: "", + expectedStatusCode: http.StatusAccepted, + }, + { + name: "UserInAllowedEmail", + email: "toto@example.com", + querystring: "?allowed_emails=toto@example.com", + expectedStatusCode: http.StatusAccepted, + }, + { + name: "UserNotInAllowedEmail", + email: "toto@example.com", + querystring: "?allowed_emails=tete@example.com", + expectedStatusCode: http.StatusForbidden, + }, + { + name: "UserNotInAllowedEmails", + email: "toto@example.com", + querystring: "?allowed_emails=tete@example.com,tutu@example.com", + expectedStatusCode: http.StatusForbidden, + }, + { + name: "UserInAllowedEmails", + email: "toto@example.com", + querystring: "?allowed_emails=tete@example.com,toto@example.com", + expectedStatusCode: http.StatusAccepted, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + groups := []string{} + + created := time.Now() + + session := &sessions.SessionState{ + Groups: groups, + Email: tc.email, + AccessToken: "oauth_token", + CreatedAt: &created, + } + + test, err := NewAuthOnlyEndpointTest(tc.querystring, func(opts *options.Options) {}) + if err != nil { + t.Fatal(err) + } + + err = test.SaveSession(session) + assert.NoError(t, err) + + test.proxy.ServeHTTP(test.rw, test.req) + + assert.Equal(t, tc.expectedStatusCode, test.rw.Code) + }) + } +}