git.iko.soy/typeable/oauth2-proxy
on master help sign in
Streamline Google to use default Authorize
commitb92fd4b0bbd14363f09e4bf8dada1094bd760b5b authorNick Meves <nick.meves@greenhouse.io> dateSat 26 Sep 2020, 19:24 (6 years ago) parenteb58ea2
6 files changed+40−44
CHANGELOG.md+9 oauthproxy_test.go−2 pkg/validation/options.go+7−1 providers/google.go+17−32 providers/google_test.go+6−8 providers/provider_data.go+1−1
CHANGELOG.md+9
@@ -6,6 +6,11 @@
66
77- [#905](https://github.com/oauth2-proxy/oauth2-proxy/pull/905) Existing sessions from v6.0.0 or earlier are no longer valid. They will trigger a reauthentication.
88- [#826](https://github.com/oauth2-proxy/oauth2-proxy/pull/826) `skip-auth-strip-headers` now applies to all requests, not just those where authentication would be skipped.
9+- [#797](https://github.com/oauth2-proxy/oauth2-proxy/pull/797) The behavior of the Google provider Groups restriction changes with this
10+ - Either `--google-group` or the new `--allowed-group` will work for Google now (`--google-group` will be used it both are set)
11+ - Group membership lists will be passed to the backend with the `X-Forwarded-Groups` header
12+ - If you change the list of allowed groups, existing sessions that now don't have a valid group will be logged out immediately.
13+ - Previously, group membership was only checked on session creation and refresh.
914- [#789](https://github.com/oauth2-proxy/oauth2-proxy/pull/789) `--skip-auth-route` is (almost) backwards compatible with `--skip-auth-regex`
1015 - We are marking `--skip-auth-regex` as DEPRECATED and will remove it in the next major version.
1116 - If your regex contains an `=` and you want it for all methods, you will need to add a leading `=` (this is the area where `--skip-auth-regex` doesn't port perfectly)
@@ -18,6 +23,9 @@
1823## Breaking Changes
1924
2025- [#911](https://github.com/oauth2-proxy/oauth2-rpoxy/pull/911) Specifying a non-existent provider will cause OAuth2-Proxy to fail on startup instead of defaulting to "google".
26+- [#797](https://github.com/oauth2-proxy/oauth2-proxy/pull/797) Security changes to Google provider group authorization flow
27+ - If you change the list of allowed groups, existing sessions that now don't have a valid group will be logged out immediately.
28+ - Previously, group membership was only checked on session creation and refresh.
2129- [#722](https://github.com/oauth2-proxy/oauth2-proxy/pull/722) When a Redis session store is configured, OAuth2-Proxy will fail to start up unless connection and health checks to Redis pass
2230- [#800](https://github.com/oauth2-proxy/oauth2-proxy/pull/800) Fix import path for v7. The import path has changed to support the go get installation.
2331 - You can now `go get github.com/oauth2-proxy/oauth2-proxy/v7` to get the latest `v7` version of OAuth2 Proxy
@@ -40,6 +48,7 @@
4048- [#905](https://github.com/oauth2-proxy/oauth2-proxy/pull/905) Remove v5 legacy sessions support (@NickMeves)
4149- [#904](https://github.com/oauth2-proxy/oauth2-proxy/pull/904) Set `skip-auth-strip-headers` to `true` by default (@NickMeves)
4250- [#826](https://github.com/oauth2-proxy/oauth2-proxy/pull/826) Integrate new header injectors into project (@JoelSpeed)
51+- [#797](https://github.com/oauth2-proxy/oauth2-proxy/pull/797) Create universal Authorization behavior across providers (@NickMeves)
4352- [#898](https://github.com/oauth2-proxy/oauth2-proxy/pull/898) Migrate documentation to Docusaurus (@JoelSpeed)
4453- [#754](https://github.com/oauth2-proxy/oauth2-proxy/pull/754) Azure token refresh (@codablock)
4554- [#825](https://github.com/oauth2-proxy/oauth2-proxy/pull/825) Fix code coverage reporting on GitHub actions(@JoelSpeed)
oauthproxy_test.go−2
@@ -1133,8 +1133,6 @@ func TestUserInfoEndpointAccepted(t *testing.T) {
11331133 Email: "john.doe@example.com", AccessToken: "my_access_token"}
11341134 err = test.SaveSession(startSession)
11351135 assert.NoError(t, err)
1136−
1137− return
11381136}
11391137
11401138func TestUserInfoEndpointUnauthorizedOnNoCookieSetError(t *testing.T) {
pkg/validation/options.go+7−1
@@ -257,7 +257,13 @@ func parseProviderInfo(o *options.Options, msgs []string) []string {
257257 if err != nil {
258258 msgs = append(msgs, "invalid Google credentials file: "+o.GoogleServiceAccountJSON)
259259 } else {
260− p.SetGroupRestriction(o.GoogleGroups, o.GoogleAdminEmail, file)
260+ groups := o.AllowedGroups
261+ // Backwards compatibility with `--google-group` option
262+ if len(o.GoogleGroups) > 0 {
263+ groups = o.GoogleGroups
264+ p.SetAllowedGroups(groups)
265+ }
266+ p.SetGroupRestriction(groups, o.GoogleAdminEmail, file)
261267 }
262268 }
263269 case *providers.BitbucketProvider:
providers/google.go+17−32
@@ -27,11 +27,14 @@ type GoogleProvider struct {
2727 *ProviderData
2828
2929 RedeemRefreshURL *url.URL
30+
3031 // GroupValidator is a function that determines if the user in the passed
3132 // session is a member of any of the configured Google groups.
32− GroupValidator func(*sessions.SessionState, bool) bool
33−
34− allowedGroups map[string]struct{}
33+ //
34+ // This hits the Google API for each group, so it is called on Redeem &
35+ // Refresh. `Authorize` uses the results of this saved in `session.Groups`
36+ // Since it is called on every request.
37+ GroupValidator func(*sessions.SessionState) bool
3538}
3639
3740var _ Provider = (*GoogleProvider)(nil)
@@ -89,7 +92,7 @@ func NewGoogleProvider(p *ProviderData) *GoogleProvider {
8992 ProviderData: p,
9093 // Set a default GroupValidator to just always return valid (true), it will
9194 // be overwritten if we configured a Google group restriction.
92− GroupValidator: func(*sessions.SessionState, bool) bool {
95+ GroupValidator: func(*sessions.SessionState) bool {
9396 return true
9497 },
9598 }
@@ -172,45 +175,27 @@ func (p *GoogleProvider) Redeem(ctx context.Context, redirectURL, code string) (
172175 Email: c.Email,
173176 User: c.Subject,
174177 }
175− p.GroupValidator(s, true)
178+ p.GroupValidator(s)
176179
177180 return s, nil
178181}
179182
180−func (p *GoogleProvider) Authorize(ctx context.Context, s *sessions.SessionState) (bool, error) {
181− return p.GroupValidator(s, false), nil
182−}
183−
184183// SetGroupRestriction configures the GoogleProvider to restrict access to the
185184// specified group(s). AdminEmail has to be an administrative email on the domain that is
186185// checked. CredentialsFile is the path to a json file containing a Google service
187186// account credentials.
188187func (p *GoogleProvider) SetGroupRestriction(groups []string, adminEmail string, credentialsReader io.Reader) {
189188 adminService := getAdminService(adminEmail, credentialsReader)
190− for _, group := range groups {
191− p.allowedGroups[group] = struct{}{}
192− }
193−
194− p.GroupValidator = func(s *sessions.SessionState, sync bool) bool {
195− if sync {
196− // Reset our saved Groups in case membership changed
197− s.Groups = make([]string, 0, len(groups))
198− for _, group := range groups {
199− if userInGroup(adminService, group, s.Email) {
200− s.Groups = append(s.Groups, group)
201− }
202− }
203− return len(s.Groups) > 0
204− }
205−
206− // Don't resync with Google, handles when OAuth2-Proxy settings
207− // alter allowed groups but existing sessions are still valid
208− for _, group := range s.Groups {
209− if _, ok := p.allowedGroups[group]; ok {
210− return true
189+ p.GroupValidator = func(s *sessions.SessionState) bool {
190+ // Reset our saved Groups in case membership changed
191+ // This is used by `Authorize` on every request
192+ s.Groups = make([]string, 0, len(groups))
193+ for _, group := range groups {
194+ if userInGroup(adminService, group, s.Email) {
195+ s.Groups = append(s.Groups, group)
211196 }
212197 }
213− return false
198+ return len(s.Groups) > 0
214199 }
215200}
216201
@@ -282,7 +267,7 @@ func (p *GoogleProvider) RefreshSessionIfNeeded(ctx context.Context, s *sessions
282267 }
283268
284269 // re-check that the user is in the proper google group(s)
285− if !p.GroupValidator(s, true) {
270+ if !p.GroupValidator(s) {
286271 return false, fmt.Errorf("%s is no longer in the group(s)", s.Email)
287272 }
288273
providers/google_test.go+6−8
@@ -10,7 +10,7 @@ import (
1010 "net/url"
1111 "testing"
1212
13− "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
13+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
1414 . "github.com/onsi/gomega"
1515 "github.com/stretchr/testify/assert"
1616 admin "google.golang.org/api/admin/directory/v1"
@@ -110,19 +110,19 @@ func TestGoogleProviderGetEmailAddress(t *testing.T) {
110110 assert.Equal(t, "refresh12345", session.RefreshToken)
111111}
112112
113−func TestGoogleProviderAuthorize(t *testing.T) {
113+func TestGoogleProviderGroupValidator(t *testing.T) {
114114 const sessionEmail = "michael.bland@gsa.gov"
115115
116116 testCases := map[string]struct {
117117 session *sessions.SessionState
118− validatorFunc func(*sessions.SessionState, bool) bool
118+ validatorFunc func(*sessions.SessionState) bool
119119 expectedAuthZ bool
120120 }{
121121 "Email is authorized with GroupValidator": {
122122 session: &sessions.SessionState{
123123 Email: sessionEmail,
124124 },
125− validatorFunc: func(s *sessions.SessionState, _ bool) bool {
125+ validatorFunc: func(s *sessions.SessionState) bool {
126126 return s.Email == sessionEmail
127127 },
128128 expectedAuthZ: true,
@@ -131,7 +131,7 @@ func TestGoogleProviderAuthorize(t *testing.T) {
131131 session: &sessions.SessionState{
132132 Email: sessionEmail,
133133 },
134− validatorFunc: func(s *sessions.SessionState, _ bool) bool {
134+ validatorFunc: func(s *sessions.SessionState) bool {
135135 return s.Email != sessionEmail
136136 },
137137 expectedAuthZ: false,
@@ -151,9 +151,7 @@ func TestGoogleProviderAuthorize(t *testing.T) {
151151 if tc.validatorFunc != nil {
152152 p.GroupValidator = tc.validatorFunc
153153 }
154− authorized, err := p.Authorize(context.Background(), tc.session)
155− g.Expect(err).ToNot(HaveOccurred())
156− g.Expect(authorized).To(Equal(tc.expectedAuthZ))
154+ g.Expect(p.GroupValidator(tc.session)).To(Equal(tc.expectedAuthZ))
157155 })
158156 }
159157}
providers/provider_data.go+1−1
@@ -52,7 +52,7 @@ func (p *ProviderData) GetClientSecret() (clientSecret string, err error) {
5252// SetAllowedGroups organizes a group list into the AllowedGroups map
5353// to be consumed by Authorize implementations
5454func (p *ProviderData) SetAllowedGroups(groups []string) {
55− p.AllowedGroups = map[string]struct{}{}
55+ p.AllowedGroups = make(map[string]struct{}, len(groups))
5656 for _, group := range groups {
5757 p.AllowedGroups[group] = struct{}{}
5858 }