diff --git a/connector/oidc/oidc.go b/connector/oidc/oidc.go index 1cf2b62a..45a0c22d 100644 --- a/connector/oidc/oidc.go +++ b/connector/oidc/oidc.go @@ -190,10 +190,19 @@ type FilterGroupClaims struct { GroupsFilter string `json:"groupsFilter"` } +// ValueOrClaim represents a value that can be either a literal string or a reference to a claim. +// If Claim is set, its value from the claims map will be used. +// Otherwise, Value (the literal string) will be used. +type ValueOrClaim struct { + Value string `json:"value,omitempty"` + Claim string `json:"claim,omitempty"` + Separator string `json:"separator,omitempty"` +} + // ModifyGroupNames allows to modify the group claims by adding a prefix and/or suffix to each group. type ModifyGroupNames struct { - Prefix string `json:"prefix"` - Suffix string `json:"suffix"` + Prefix ValueOrClaim `json:"prefix"` + Suffix ValueOrClaim `json:"suffix"` } // Domains that don't support basic auth. golang.org/x/oauth2 has an internal @@ -314,8 +323,7 @@ func (c *Config) Open(id string, logger *slog.Logger) (conn connector.Connector, groupsKey: c.ClaimMapping.GroupsKey, newGroupFromClaims: c.ClaimMutations.NewGroupFromClaims, groupsFilter: groupsFilter, - groupsPrefix: c.ClaimMutations.ModifyGroupNames.Prefix, - groupsSuffix: c.ClaimMutations.ModifyGroupNames.Suffix, + modifyGroupNames: c.ClaimMutations.ModifyGroupNames, }, nil } @@ -346,8 +354,7 @@ type oidcConnector struct { groupsKey string newGroupFromClaims []NewGroupFromClaims groupsFilter *regexp.Regexp - groupsPrefix string - groupsSuffix string + modifyGroupNames ModifyGroupNames } func (c *oidcConnector) Close() error { @@ -441,6 +448,28 @@ func (c *oidcConnector) TokenIdentity(ctx context.Context, subjectTokenType, sub return c.createIdentity(ctx, identity, token, exchangeCaller) } +// resolveValueOrClaim resolves either a literal value or a claim reference. +// isPrefix indicates if this is for a prefix (true) or suffix (false). +func resolveValueOrClaim(v ValueOrClaim, claims map[string]interface{}, isPrefix bool) string { + var result string + if v.Claim != "" { + if s, ok := claims[v.Claim].(string); ok { + result = s + } + } else { + result = v.Value + } + + if result != "" && v.Separator != "" { + if isPrefix { + result += v.Separator + } else { + result = v.Separator + result + } + } + return result +} + func (c *oidcConnector) createIdentity(ctx context.Context, identity connector.Identity, token *oauth2.Token, caller caller) (connector.Identity, error) { var claims map[string]interface{} @@ -588,9 +617,12 @@ func (c *oidcConnector) createIdentity(ctx context.Context, identity connector.I } // add prefix/suffix to groups - if c.groupsPrefix != "" || c.groupsSuffix != "" { + if c.modifyGroupNames.Prefix.Value != "" || c.modifyGroupNames.Prefix.Claim != "" || + c.modifyGroupNames.Suffix.Value != "" || c.modifyGroupNames.Suffix.Claim != "" { for i, group := range groups { - groups[i] = c.groupsPrefix + group + c.groupsSuffix + prefix := resolveValueOrClaim(c.modifyGroupNames.Prefix, claims, true) + suffix := resolveValueOrClaim(c.modifyGroupNames.Suffix, claims, false) + groups[i] = prefix + group + suffix } } diff --git a/connector/oidc/oidc_test.go b/connector/oidc/oidc_test.go index 9a3d7126..a287d377 100644 --- a/connector/oidc/oidc_test.go +++ b/connector/oidc/oidc_test.go @@ -65,8 +65,7 @@ func TestHandleCallback(t *testing.T) { token map[string]interface{} groupsRegex string newGroupFromClaims []NewGroupFromClaims - groupsPrefix string - groupsSuffix string + modifyGroupNames ModifyGroupNames }{ { name: "simpleCase", @@ -406,7 +405,9 @@ func TestHandleCallback(t *testing.T) { expectUserName: "namevalue", expectGroups: []string{"prefix-group1", "prefix-group2", "prefix-groupA", "prefix-groupB"}, expectedEmailField: "emailvalue", - groupsPrefix: "prefix-", + modifyGroupNames: ModifyGroupNames{ + Prefix: ValueOrClaim{Value: "prefix-"}, + }, token: map[string]interface{}{ "sub": "subvalue", "name": "namevalue", @@ -423,7 +424,9 @@ func TestHandleCallback(t *testing.T) { expectUserName: "namevalue", expectGroups: []string{"group1-suffix", "group2-suffix", "groupA-suffix", "groupB-suffix"}, expectedEmailField: "emailvalue", - groupsSuffix: "-suffix", + modifyGroupNames: ModifyGroupNames{ + Suffix: ValueOrClaim{Value: "-suffix"}, + }, token: map[string]interface{}{ "sub": "subvalue", "name": "namevalue", @@ -440,8 +443,10 @@ func TestHandleCallback(t *testing.T) { expectUserName: "namevalue", expectGroups: []string{"prefix-group1-suffix", "prefix-group2-suffix", "prefix-groupA-suffix", "prefix-groupB-suffix"}, expectedEmailField: "emailvalue", - groupsPrefix: "prefix-", - groupsSuffix: "-suffix", + modifyGroupNames: ModifyGroupNames{ + Prefix: ValueOrClaim{Value: "prefix-"}, + Suffix: ValueOrClaim{Value: "-suffix"}, + }, token: map[string]interface{}{ "sub": "subvalue", "name": "namevalue", @@ -521,8 +526,7 @@ func TestHandleCallback(t *testing.T) { config.ClaimMapping.GroupsKey = tc.groupsKey config.ClaimMutations.NewGroupFromClaims = tc.newGroupFromClaims config.ClaimMutations.FilterGroupClaims.GroupsFilter = tc.groupsRegex - config.ClaimMutations.ModifyGroupNames.Prefix = tc.groupsPrefix - config.ClaimMutations.ModifyGroupNames.Suffix = tc.groupsSuffix + config.ClaimMutations.ModifyGroupNames = tc.modifyGroupNames conn, err := newConnector(config) if err != nil {