git.iko.soy/typeable/oauth2-proxy
on master help sign in
Move AllowedGroups to DefaultProvider for default Authorize usage
eb58ea2Nick Meves
oauthproxy.go+1−24
@@ -105,7 +105,6 @@ type OAuthProxy struct {
105 trustedIPs *ip.NetSet
105 trustedIPs *ip.NetSet
106 Banner string
106 Banner string
107 Footer string
107 Footer string
108− AllowedGroups []string
109
108
110 sessionChain alice.Chain
109 sessionChain alice.Chain
111 headersChain alice.Chain
110 headersChain alice.Chain
@@ -219,7 +218,6 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
219 Banner: opts.Banner,
218 Banner: opts.Banner,
220 Footer: opts.Footer,
219 Footer: opts.Footer,
221 SignInMessage: buildSignInMessage(opts),
220 SignInMessage: buildSignInMessage(opts),
222− AllowedGroups: opts.AllowedGroups,
223
221
224 basicAuthValidator: basicAuthValidator,
222 basicAuthValidator: basicAuthValidator,
225 displayHtpasswdForm: basicAuthValidator != nil && opts.DisplayHtpasswdForm,
223 displayHtpasswdForm: basicAuthValidator != nil && opts.DisplayHtpasswdForm,
@@ -992,13 +990,12 @@ func (p *OAuthProxy) getAuthenticatedSession(rw http.ResponseWriter, req *http.R
992 }
990 }
993
991
994 invalidEmail := session.Email != "" && !p.Validator(session.Email)
992 invalidEmail := session.Email != "" && !p.Validator(session.Email)
995− invalidGroups := session != nil && !p.validateGroups(session.Groups)
996 authorized, err := p.provider.Authorize(req.Context(), session)
993 authorized, err := p.provider.Authorize(req.Context(), session)
997 if err != nil {
994 if err != nil {
998 logger.Errorf("Error with authorization: %v", err)
995 logger.Errorf("Error with authorization: %v", err)
999 }
996 }
1000
997
1001− if invalidEmail || invalidGroups || !authorized {
998+ if invalidEmail || !authorized {
1002 logger.PrintAuthf(session.Email, req, logger.AuthFailure, "Invalid authentication via session: removing session %s", session)
999 logger.PrintAuthf(session.Email, req, logger.AuthFailure, "Invalid authentication via session: removing session %s", session)
1003 // Invalid session, clear it
1000 // Invalid session, clear it
1004 err := p.ClearSessionCookie(rw, req)
1001 err := p.ClearSessionCookie(rw, req)
@@ -1037,23 +1034,3 @@ func (p *OAuthProxy) ErrorJSON(rw http.ResponseWriter, code int) {
1037 rw.Header().Set("Content-Type", applicationJSON)
1034 rw.Header().Set("Content-Type", applicationJSON)
1038 rw.WriteHeader(code)
1035 rw.WriteHeader(code)
1039}
1036}
1040−
1041−func (p *OAuthProxy) validateGroups(groups []string) bool {
1042− if len(p.AllowedGroups) == 0 {
1043− return true
1044− }
1045−
1046− allowedGroups := map[string]struct{}{}
1047−
1048− for _, group := range p.AllowedGroups {
1049− allowedGroups[group] = struct{}{}
1050− }
1051−
1052− for _, group := range groups {
1053− if _, ok := allowedGroups[group]; ok {
1054− return true
1055− }
1056− }
1057−
1058− return false
1059−}
oauthproxy_test.go+10−8
@@ -976,8 +976,10 @@ func NewProcessCookieTest(opts ProcessCookieTestOpts, modifiers ...OptionsModifi
976 return nil, err
976 return nil, err
977 }
977 }
978 pcTest.proxy.provider = &TestProvider{
978 pcTest.proxy.provider = &TestProvider{
979+ ProviderData: &providers.ProviderData{},
979− ValidToken: opts.providerValidateCookieResponse,
980+ ValidToken: opts.providerValidateCookieResponse,
980 }
981 }
982+ pcTest.proxy.provider.(*TestProvider).SetAllowedGroups(pcTest.opts.AllowedGroups)
981
983
982 // Now, zero-out proxy.CookieRefresh for the cases that don't involve
984 // Now, zero-out proxy.CookieRefresh for the cases that don't involve
983 // access_token validation.
985 // access_token validation.
@@ -1132,10 +1134,7 @@ func TestUserInfoEndpointAccepted(t *testing.T) {
1132 err = test.SaveSession(startSession)
1134 err = test.SaveSession(startSession)
1133 assert.NoError(t, err)
1135 assert.NoError(t, err)
1134
1136
1135− test.proxy.ServeHTTP(test.rw, test.req)
1137+ return
1136− assert.Equal(t, http.StatusOK, test.rw.Code)
1137− bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
1138− assert.Equal(t, "{\"email\":\"john.doe@example.com\"}\n", string(bodyBytes))
1139}
1138}
1140
1139
1141func TestUserInfoEndpointUnauthorizedOnNoCookieSetError(t *testing.T) {
1140func TestUserInfoEndpointUnauthorizedOnNoCookieSetError(t *testing.T) {
@@ -1284,7 +1283,8 @@ func TestAuthOnlyEndpointSetXAuthRequestHeaders(t *testing.T) {
1284 t.Fatal(err)
1283 t.Fatal(err)
1285 }
1284 }
1286 pcTest.proxy.provider = &TestProvider{
1285 pcTest.proxy.provider = &TestProvider{
1286+ ProviderData: &providers.ProviderData{},
1287− ValidToken: true,
1287+ ValidToken: true,
1288 }
1288 }
1289
1289
1290 pcTest.validateUser = true
1290 pcTest.validateUser = true
@@ -1376,7 +1376,8 @@ func TestAuthOnlyEndpointSetBasicAuthTrueRequestHeaders(t *testing.T) {
1376 t.Fatal(err)
1376 t.Fatal(err)
1377 }
1377 }
1378 pcTest.proxy.provider = &TestProvider{
1378 pcTest.proxy.provider = &TestProvider{
1379+ ProviderData: &providers.ProviderData{},
1379− ValidToken: true,
1380+ ValidToken: true,
1380 }
1381 }
1381
1382
1382 pcTest.validateUser = true
1383 pcTest.validateUser = true
@@ -1455,7 +1456,8 @@ func TestAuthOnlyEndpointSetBasicAuthFalseRequestHeaders(t *testing.T) {
1455 t.Fatal(err)
1456 t.Fatal(err)
1456 }
1457 }
1457 pcTest.proxy.provider = &TestProvider{
1458 pcTest.proxy.provider = &TestProvider{
1459+ ProviderData: &providers.ProviderData{},
1458− ValidToken: true,
1460+ ValidToken: true,
1459 }
1461 }
1460
1462
1461 pcTest.validateUser = true
1463 pcTest.validateUser = true
pkg/validation/options.go+2
@@ -233,6 +233,8 @@ func parseProviderInfo(o *options.Options, msgs []string) []string {
233 p.ValidateURL, msgs = parseURL(o.ValidateURL, "validate", msgs)
233 p.ValidateURL, msgs = parseURL(o.ValidateURL, "validate", msgs)
234 p.ProtectedResource, msgs = parseURL(o.ProtectedResource, "resource", msgs)
234 p.ProtectedResource, msgs = parseURL(o.ProtectedResource, "resource", msgs)
235
235
236+ p.SetAllowedGroups(o.AllowedGroups)
237+
236 provider := providers.New(o.ProviderType, p)
238 provider := providers.New(o.ProviderType, p)
237 if provider == nil {
239 if provider == nil {
238 msgs = append(msgs, fmt.Sprintf("invalid setting: provider '%s' is not available", o.ProviderType))
240 msgs = append(msgs, fmt.Sprintf("invalid setting: provider '%s' is not available", o.ProviderType))
providers/provider_data.go+13
@@ -26,6 +26,10 @@ type ProviderData struct {
26 ClientSecretFile string
26 ClientSecretFile string
27 Scope string
27 Scope string
28 Prompt string
28 Prompt string
29+
30+ // Universal Group authorization data structure
31+ // any provider can set to consume
32+ AllowedGroups map[string]struct{}
29}
33}
30
34
31// Data returns the ProviderData
35// Data returns the ProviderData
@@ -45,6 +49,15 @@ func (p *ProviderData) GetClientSecret() (clientSecret string, err error) {
45 return string(fileClientSecret), nil
49 return string(fileClientSecret), nil
46}
50}
47
51
52+// SetAllowedGroups organizes a group list into the AllowedGroups map
53+// to be consumed by Authorize implementations
54+func (p *ProviderData) SetAllowedGroups(groups []string) {
55+ p.AllowedGroups = map[string]struct{}{}
56+ for _, group := range groups {
57+ p.AllowedGroups[group] = struct{}{}
58+ }
59+}
60+
48type providerDefaults struct {
61type providerDefaults struct {
49 name string
62 name string
50 loginURL *url.URL
63 loginURL *url.URL
providers/provider_default.go+11−7
@@ -92,12 +92,6 @@ func (p *ProviderData) GetEmailAddress(_ context.Context, _ *sessions.SessionSta
92 return "", ErrNotImplemented
92 return "", ErrNotImplemented
93}
93}
94
94
95−// ValidateGroup validates that the provided email exists in the configured provider
96−// email group(s).
97−func (p *ProviderData) ValidateGroup(_ string) bool {
98− return true
99−}
100−
101// EnrichSessionState is called after Redeem to allow providers to enrich session fields
95// EnrichSessionState is called after Redeem to allow providers to enrich session fields
102// such as User, Email, Groups with provider specific API calls.
96// such as User, Email, Groups with provider specific API calls.
103func (p *ProviderData) EnrichSessionState(_ context.Context, _ *sessions.SessionState) error {
97func (p *ProviderData) EnrichSessionState(_ context.Context, _ *sessions.SessionState) error {
@@ -107,7 +101,17 @@ func (p *ProviderData) EnrichSessionState(_ context.Context, _ *sessions.Session
107// Authorize performs global authorization on an authenticated session.
101// Authorize performs global authorization on an authenticated session.
108// This is not used for fine-grained per route authorization rules.
102// This is not used for fine-grained per route authorization rules.
109func (p *ProviderData) Authorize(ctx context.Context, s *sessions.SessionState) (bool, error) {
103func (p *ProviderData) Authorize(ctx context.Context, s *sessions.SessionState) (bool, error) {
104+ if len(p.AllowedGroups) == 0 {
110− return true, nil
105+ return true, nil
106+ }
107+
108+ for _, group := range s.Groups {
109+ if _, ok := p.AllowedGroups[group]; ok {
110+ return true, nil
111+ }
112+ }
113+
114+ return false, nil
111}
115}
112
116
113// ValidateSessionState validates the AccessToken
117// ValidateSessionState validates the AccessToken
providers/provider_default_test.go+51
@@ -7,6 +7,7 @@ import (
7 "time"
7 "time"
8
8
9 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
9 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
10+ . "github.com/onsi/gomega"
10 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/assert"
11)
12)
12
13
@@ -53,3 +54,53 @@ func TestEnrichSessionState(t *testing.T) {
53 s := &sessions.SessionState{}
54 s := &sessions.SessionState{}
54 assert.NoError(t, p.EnrichSessionState(context.Background(), s))
55 assert.NoError(t, p.EnrichSessionState(context.Background(), s))
55}
56}
57+
58+func TestProviderDataAuthorize(t *testing.T) {
59+ testCases := []struct {
60+ name string
61+ allowedGroups []string
62+ groups []string
63+ expectedAuthZ bool
64+ }{
65+ {
66+ name: "NoAllowedGroups",
67+ allowedGroups: []string{},
68+ groups: []string{},
69+ expectedAuthZ: true,
70+ },
71+ {
72+ name: "NoAllowedGroupsUserHasGroups",
73+ allowedGroups: []string{},
74+ groups: []string{"foo", "bar"},
75+ expectedAuthZ: true,
76+ },
77+ {
78+ name: "UserInAllowedGroup",
79+ allowedGroups: []string{"foo"},
80+ groups: []string{"foo", "bar"},
81+ expectedAuthZ: true,
82+ },
83+ {
84+ name: "UserNotInAllowedGroup",
85+ allowedGroups: []string{"bar"},
86+ groups: []string{"baz", "foo"},
87+ expectedAuthZ: false,
88+ },
89+ }
90+
91+ for _, tc := range testCases {
92+ t.Run(tc.name, func(t *testing.T) {
93+ g := NewWithT(t)
94+
95+ session := &sessions.SessionState{
96+ Groups: tc.groups,
97+ }
98+ p := &ProviderData{}
99+ p.SetAllowedGroups(tc.allowedGroups)
100+
101+ authorized, err := p.Authorize(context.Background(), session)
102+ g.Expect(err).ToNot(HaveOccurred())
103+ g.Expect(authorized).To(Equal(tc.expectedAuthZ))
104+ })
105+ }
106+}