Merge a7937a81a7 into 110d51d1d7
				
					
				
			This commit is contained in:
		
						commit
						f5a8d15807
					
				|  | @ -25,6 +25,7 @@ func CreateTokenToSessionFunc(verify VerifyFunc) TokenToSessionFunc { | ||||||
| 			Verified          *bool    `json:"email_verified"` | 			Verified          *bool    `json:"email_verified"` | ||||||
| 			PreferredUsername string   `json:"preferred_username"` | 			PreferredUsername string   `json:"preferred_username"` | ||||||
| 			Groups            []string `json:"groups"` | 			Groups            []string `json:"groups"` | ||||||
|  | 			ACR               string   `json:"acr"` | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		idToken, err := verify(ctx, token) | 		idToken, err := verify(ctx, token) | ||||||
|  | @ -49,6 +50,7 @@ func CreateTokenToSessionFunc(verify VerifyFunc) TokenToSessionFunc { | ||||||
| 			User:              claims.Subject, | 			User:              claims.Subject, | ||||||
| 			Groups:            claims.Groups, | 			Groups:            claims.Groups, | ||||||
| 			PreferredUsername: claims.PreferredUsername, | 			PreferredUsername: claims.PreferredUsername, | ||||||
|  | 			ACR:               claims.ACR, | ||||||
| 			AccessToken:       token, | 			AccessToken:       token, | ||||||
| 			IDToken:           token, | 			IDToken:           token, | ||||||
| 			RefreshToken:      "", | 			RefreshToken:      "", | ||||||
|  |  | ||||||
|  | @ -272,6 +272,8 @@ type OIDCOptions struct { | ||||||
| 	// ExtraAudiences is a list of additional audiences that are allowed
 | 	// ExtraAudiences is a list of additional audiences that are allowed
 | ||||||
| 	// to pass verification in addition to the client id.
 | 	// to pass verification in addition to the client id.
 | ||||||
| 	ExtraAudiences []string `json:"extraAudiences,omitempty"` | 	ExtraAudiences []string `json:"extraAudiences,omitempty"` | ||||||
|  | 	// to pass acr values to the provider
 | ||||||
|  | 	ACRs string `json:"acr,omitempty"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type LoginGovOptions struct { | type LoginGovOptions struct { | ||||||
|  |  | ||||||
|  | @ -27,6 +27,7 @@ type SessionState struct { | ||||||
| 	User              string   `msgpack:"u,omitempty"` | 	User              string   `msgpack:"u,omitempty"` | ||||||
| 	Groups            []string `msgpack:"g,omitempty"` | 	Groups            []string `msgpack:"g,omitempty"` | ||||||
| 	PreferredUsername string   `msgpack:"pu,omitempty"` | 	PreferredUsername string   `msgpack:"pu,omitempty"` | ||||||
|  | 	ACR               string   `msgpack:"acr,omitempty"` | ||||||
| 
 | 
 | ||||||
| 	// Internal helpers, not serialized
 | 	// Internal helpers, not serialized
 | ||||||
| 	Clock func() time.Time `msgpack:"-"` // override for time.Now, for testing
 | 	Clock func() time.Time `msgpack:"-"` // override for time.Now, for testing
 | ||||||
|  | @ -154,6 +155,8 @@ func (s *SessionState) GetClaim(claim string) []string { | ||||||
| 		return groups | 		return groups | ||||||
| 	case "preferred_username": | 	case "preferred_username": | ||||||
| 		return []string{s.PreferredUsername} | 		return []string{s.PreferredUsername} | ||||||
|  | 	case "acr": | ||||||
|  | 		return []string{s.ACR} | ||||||
| 	default: | 	default: | ||||||
| 		return []string{} | 		return []string{} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -46,6 +46,7 @@ func NewOIDCProvider(p *ProviderData, opts options.OIDCOptions) *OIDCProvider { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	p.setProviderDefaults(oidcProviderDefaults) | 	p.setProviderDefaults(oidcProviderDefaults) | ||||||
|  | 	p.setAllowedACR(opts.ACRs) | ||||||
| 	p.getAuthorizationHeaderFunc = makeOIDCHeader | 	p.getAuthorizationHeaderFunc = makeOIDCHeader | ||||||
| 
 | 
 | ||||||
| 	return &OIDCProvider{ | 	return &OIDCProvider{ | ||||||
|  |  | ||||||
|  | @ -22,6 +22,7 @@ import ( | ||||||
| const ( | const ( | ||||||
| 	// This is not exported as it's not currently user configurable
 | 	// This is not exported as it's not currently user configurable
 | ||||||
| 	oidcUserClaim = "sub" | 	oidcUserClaim = "sub" | ||||||
|  | 	oidcAcrClaim  = "acr" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // ProviderData contains information required to configure all implementations
 | // ProviderData contains information required to configure all implementations
 | ||||||
|  | @ -55,6 +56,7 @@ type ProviderData struct { | ||||||
| 	// Universal Group authorization data structure
 | 	// Universal Group authorization data structure
 | ||||||
| 	// any provider can set to consume
 | 	// any provider can set to consume
 | ||||||
| 	AllowedGroups map[string]struct{} | 	AllowedGroups map[string]struct{} | ||||||
|  | 	AllowedACRs   map[string]struct{} | ||||||
| 
 | 
 | ||||||
| 	getAuthorizationHeaderFunc func(string) http.Header | 	getAuthorizationHeaderFunc func(string) http.Header | ||||||
| 	loginURLParameterDefaults  url.Values | 	loginURLParameterDefaults  url.Values | ||||||
|  | @ -183,6 +185,14 @@ func (p *ProviderData) setAllowedGroups(groups []string) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (p *ProviderData) setAllowedACR(acrs string) { | ||||||
|  | 	p.AllowedACRs = make(map[string]struct{}) | ||||||
|  | 	var resultingACRs = strings.Split(acrs, ",") | ||||||
|  | 	for _, acr := range resultingACRs { | ||||||
|  | 		p.AllowedACRs[acr] = struct{}{} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type providerDefaults struct { | type providerDefaults struct { | ||||||
| 	name        string | 	name        string | ||||||
| 	loginURL    *url.URL | 	loginURL    *url.URL | ||||||
|  | @ -260,6 +270,7 @@ func (p *ProviderData) buildSessionFromClaims(rawIDToken, accessToken string) (* | ||||||
| 		{p.UserClaim, &ss.User}, | 		{p.UserClaim, &ss.User}, | ||||||
| 		{p.EmailClaim, &ss.Email}, | 		{p.EmailClaim, &ss.Email}, | ||||||
| 		{p.GroupsClaim, &ss.Groups}, | 		{p.GroupsClaim, &ss.Groups}, | ||||||
|  | 		{oidcAcrClaim, &ss.ACR}, | ||||||
| 		// TODO (@NickMeves) Deprecate for dynamic claim to session mapping
 | 		// TODO (@NickMeves) Deprecate for dynamic claim to session mapping
 | ||||||
| 		{"preferred_username", &ss.PreferredUsername}, | 		{"preferred_username", &ss.PreferredUsername}, | ||||||
| 	} { | 	} { | ||||||
|  |  | ||||||
|  | @ -121,6 +121,12 @@ func (p *ProviderData) EnrichSession(_ context.Context, _ *sessions.SessionState | ||||||
| // Authorize performs global authorization on an authenticated session.
 | // Authorize performs global authorization on an authenticated session.
 | ||||||
| // This is not used for fine-grained per route authorization rules.
 | // This is not used for fine-grained per route authorization rules.
 | ||||||
| func (p *ProviderData) Authorize(_ context.Context, s *sessions.SessionState) (bool, error) { | func (p *ProviderData) Authorize(_ context.Context, s *sessions.SessionState) (bool, error) { | ||||||
|  | 	if len(p.AllowedACRs) > 0 { | ||||||
|  | 		if _, ok := p.AllowedACRs[s.ACR]; !ok { | ||||||
|  | 			return false, nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if len(p.AllowedGroups) == 0 { | 	if len(p.AllowedGroups) == 0 { | ||||||
| 		return true, nil | 		return true, nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -76,6 +76,8 @@ func TestProviderDataAuthorize(t *testing.T) { | ||||||
| 		name          string | 		name          string | ||||||
| 		allowedGroups []string | 		allowedGroups []string | ||||||
| 		groups        []string | 		groups        []string | ||||||
|  | 		acr           string | ||||||
|  | 		userAcr       string | ||||||
| 		expectedAuthZ bool | 		expectedAuthZ bool | ||||||
| 	}{ | 	}{ | ||||||
| 		{ | 		{ | ||||||
|  | @ -102,6 +104,23 @@ func TestProviderDataAuthorize(t *testing.T) { | ||||||
| 			groups:        []string{"baz", "foo"}, | 			groups:        []string{"baz", "foo"}, | ||||||
| 			expectedAuthZ: false, | 			expectedAuthZ: false, | ||||||
| 		}, | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:          "UserNotAllowedForACRLevel", | ||||||
|  | 			acr:           "1", | ||||||
|  | 			expectedAuthZ: false, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:          "UserNotAllowedForACRLevel", | ||||||
|  | 			acr:           "1", | ||||||
|  | 			userAcr:       "1", | ||||||
|  | 			expectedAuthZ: true, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:          "UserNotAllowedForACRLevel", | ||||||
|  | 			acr:           "2", | ||||||
|  | 			userAcr:       "somethingElse", | ||||||
|  | 			expectedAuthZ: false, | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, tc := range testCases { | 	for _, tc := range testCases { | ||||||
|  | @ -110,9 +129,11 @@ func TestProviderDataAuthorize(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| 			session := &sessions.SessionState{ | 			session := &sessions.SessionState{ | ||||||
| 				Groups: tc.groups, | 				Groups: tc.groups, | ||||||
|  | 				ACR:    tc.userAcr, | ||||||
| 			} | 			} | ||||||
| 			p := &ProviderData{} | 			p := &ProviderData{} | ||||||
| 			p.setAllowedGroups(tc.allowedGroups) | 			p.setAllowedGroups(tc.allowedGroups) | ||||||
|  | 			p.setAllowedACR(tc.acr) | ||||||
| 
 | 
 | ||||||
| 			authorized, err := p.Authorize(context.Background(), session) | 			authorized, err := p.Authorize(context.Background(), session) | ||||||
| 			g.Expect(err).ToNot(HaveOccurred()) | 			g.Expect(err).ToNot(HaveOccurred()) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue