This commit is contained in:
SamTV12345 2025-10-28 10:25:15 +01:00 committed by GitHub
commit f5a8d15807
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 46 additions and 0 deletions

View File

@ -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: "",

View File

@ -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 {

View File

@ -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{}
} }

View File

@ -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{

View File

@ -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},
} { } {

View File

@ -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
} }

View File

@ -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())