From f7d34b2b0fe3b0a6df8f48fbb3941d30e4b75f10 Mon Sep 17 00:00:00 2001 From: Mathias Petermann Date: Fri, 18 Jul 2025 13:46:32 +0200 Subject: [PATCH] feat: Add ModifyGroupNames claimMutation to oidc connector (#4144) Signed-off-by: Mathias Petermann --- connector/oidc/oidc.go | 18 ++++++++++++ connector/oidc/oidc_test.go | 56 +++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/connector/oidc/oidc.go b/connector/oidc/oidc.go index 99f20f0d..1cf2b62a 100644 --- a/connector/oidc/oidc.go +++ b/connector/oidc/oidc.go @@ -104,6 +104,7 @@ type Config struct { ClaimMutations struct { NewGroupFromClaims []NewGroupFromClaims `json:"newGroupFromClaims"` FilterGroupClaims FilterGroupClaims `json:"filterGroupClaims"` + ModifyGroupNames ModifyGroupNames `json:"modifyGroupNames"` } `json:"claimModifications"` } @@ -189,6 +190,12 @@ type FilterGroupClaims struct { GroupsFilter string `json:"groupsFilter"` } +// 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"` +} + // Domains that don't support basic auth. golang.org/x/oauth2 has an internal // list, but it only matches specific URLs, not top level domains. var brokenAuthHeaderDomains = []string{ @@ -307,6 +314,8 @@ 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, }, nil } @@ -337,6 +346,8 @@ type oidcConnector struct { groupsKey string newGroupFromClaims []NewGroupFromClaims groupsFilter *regexp.Regexp + groupsPrefix string + groupsSuffix string } func (c *oidcConnector) Close() error { @@ -576,6 +587,13 @@ func (c *oidcConnector) createIdentity(ctx context.Context, identity connector.I } } + // add prefix/suffix to groups + if c.groupsPrefix != "" || c.groupsSuffix != "" { + for i, group := range groups { + groups[i] = c.groupsPrefix + group + c.groupsSuffix + } + } + for _, config := range c.newGroupFromClaims { newGroupSegments := []string{ config.Prefix, diff --git a/connector/oidc/oidc_test.go b/connector/oidc/oidc_test.go index aa33bc83..9a3d7126 100644 --- a/connector/oidc/oidc_test.go +++ b/connector/oidc/oidc_test.go @@ -65,6 +65,8 @@ func TestHandleCallback(t *testing.T) { token map[string]interface{} groupsRegex string newGroupFromClaims []NewGroupFromClaims + groupsPrefix string + groupsSuffix string }{ { name: "simpleCase", @@ -396,6 +398,58 @@ func TestHandleCallback(t *testing.T) { "non-string-claim2": 666, }, }, + { + name: "prefixGroupNames", + userIDKey: "", // not configured + userNameKey: "", // not configured + expectUserID: "subvalue", + expectUserName: "namevalue", + expectGroups: []string{"prefix-group1", "prefix-group2", "prefix-groupA", "prefix-groupB"}, + expectedEmailField: "emailvalue", + groupsPrefix: "prefix-", + token: map[string]interface{}{ + "sub": "subvalue", + "name": "namevalue", + "groups": []string{"group1", "group2", "groupA", "groupB"}, + "email": "emailvalue", + "email_verified": true, + }, + }, + { + name: "suffixGroupNames", + userIDKey: "", // not configured + userNameKey: "", // not configured + expectUserID: "subvalue", + expectUserName: "namevalue", + expectGroups: []string{"group1-suffix", "group2-suffix", "groupA-suffix", "groupB-suffix"}, + expectedEmailField: "emailvalue", + groupsSuffix: "-suffix", + token: map[string]interface{}{ + "sub": "subvalue", + "name": "namevalue", + "groups": []string{"group1", "group2", "groupA", "groupB"}, + "email": "emailvalue", + "email_verified": true, + }, + }, + { + name: "preAndSuffixGroupNames", + userIDKey: "", // not configured + userNameKey: "", // not configured + expectUserID: "subvalue", + expectUserName: "namevalue", + expectGroups: []string{"prefix-group1-suffix", "prefix-group2-suffix", "prefix-groupA-suffix", "prefix-groupB-suffix"}, + expectedEmailField: "emailvalue", + groupsPrefix: "prefix-", + groupsSuffix: "-suffix", + token: map[string]interface{}{ + "sub": "subvalue", + "name": "namevalue", + "groups": []string{"group1", "group2", "groupA", "groupB"}, + "email": "emailvalue", + "email_verified": true, + }, + }, { name: "filterGroupClaims", userIDKey: "", // not configured @@ -467,6 +521,8 @@ 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 conn, err := newConnector(config) if err != nil {