From 0a85a97ba9d85fa0de8542a68f1edbc53ed77c25 Mon Sep 17 00:00:00 2001 From: Martijn Date: Thu, 23 Apr 2020 20:14:15 +0200 Subject: [PATCH] Allow preferred_username claim to be set for Crowd connector (#1684) * Add atlassiancrowd connector to list in readme * Add TestIdentityFromCrowdUser * Set preferred_username claim when configured * Add preferredUsernameField option to docs * Log warning when mapping invalid crowd field --- Documentation/connectors/atlassian-crowd.md | 5 ++ README.md | 1 + connector/atlassiancrowd/atlassiancrowd.go | 19 ++++++++ .../atlassiancrowd/atlassiancrowd_test.go | 47 +++++++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/Documentation/connectors/atlassian-crowd.md b/Documentation/connectors/atlassian-crowd.md index 3a5f0712..8dbaef80 100644 --- a/Documentation/connectors/atlassian-crowd.md +++ b/Documentation/connectors/atlassian-crowd.md @@ -36,4 +36,9 @@ connectors: - my-group # Prompt for username field. usernamePrompt: Login + # Optionally set preferred_username claim. + # If `preferredUsernameField` is omitted or contains an invalid option, the `preferred_username` claim will be empty. + # If `preferredUsernameField` is set, the `preferred_username` claim will be set to the chosen Crowd user attribute value. + # Possible choices are: "key", "name", "email" + preferredUsernameField: name ``` diff --git a/README.md b/README.md index 65384cda..bbfb4f29 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ Dex implements the following connectors: | [AuthProxy](Documentation/connectors/authproxy.md) | no | no | no | alpha | Authentication proxies such as Apache2 mod_auth, etc. | | [Bitbucket Cloud](Documentation/connectors/bitbucketcloud.md) | yes | yes | no | alpha | | | [OpenShift](Documentation/connectors/openshift.md) | no | yes | no | stable | | +| [Atlassian Crowd](Documentation/connectors/atlassiancrowd.md) | yes | yes | yes *) | beta | preferred_username claim must be configured through config | Stable, beta, and alpha are defined as: diff --git a/connector/atlassiancrowd/atlassiancrowd.go b/connector/atlassiancrowd/atlassiancrowd.go index d835d4cf..928e69a6 100644 --- a/connector/atlassiancrowd/atlassiancrowd.go +++ b/connector/atlassiancrowd/atlassiancrowd.go @@ -35,6 +35,7 @@ import ( // - admin // # Prompt for username field // usernamePrompt: Login +// preferredUsernameField: name // type Config struct { BaseURL string `json:"baseURL"` @@ -42,6 +43,11 @@ type Config struct { ClientSecret string `json:"clientSecret"` Groups []string `json:"groups"` + // PreferredUsernameField allows users to set the field to any of the + // following values: "key", "name" or "email". + // If unset, the preferred_username field will remain empty. + PreferredUsernameField string `json:"preferredUsernameField"` + // UsernamePrompt allows users to override the username attribute (displayed // in the username/password prompt). If unset, the handler will use. // "Username". @@ -368,6 +374,19 @@ func (c *crowdConnector) identityFromCrowdUser(user crowdUser) (connector.Identi EmailVerified: true, } + switch c.PreferredUsernameField { + case "key": + identity.PreferredUsername = user.Key + case "name": + identity.PreferredUsername = user.Name + case "email": + identity.PreferredUsername = user.Email + default: + if c.PreferredUsernameField != "" { + c.logger.Warnf("preferred_username left empty. Invalid crowd field mapped to preferred_username: %s", c.PreferredUsernameField) + } + } + return identity, nil } diff --git a/connector/atlassiancrowd/atlassiancrowd_test.go b/connector/atlassiancrowd/atlassiancrowd_test.go index 966f5b6f..213c1829 100644 --- a/connector/atlassiancrowd/atlassiancrowd_test.go +++ b/connector/atlassiancrowd/atlassiancrowd_test.go @@ -104,6 +104,53 @@ func TestUserPassword(t *testing.T) { expectNil(t, err) } +func TestIdentityFromCrowdUser(t *testing.T) { + user := crowdUser{ + Key: "12345", + Name: "testuser", + Active: true, + Email: "testuser@example.com", + } + + c := newTestCrowdConnector("/") + + // Sanity checks + expectEquals(t, user.Name, "testuser") + expectEquals(t, user.Email, "testuser@example.com") + + // Test unconfigured behaviour + i, err := c.identityFromCrowdUser(user) + expectNil(t, err) + expectEquals(t, i.UserID, "12345") + expectEquals(t, i.Username, "testuser") + expectEquals(t, i.Email, "testuser@example.com") + expectEquals(t, i.EmailVerified, true) + + // Test for various PreferredUsernameField settings + // unset + expectEquals(t, i.PreferredUsername, "") + + c.Config.PreferredUsernameField = "key" + i, err = c.identityFromCrowdUser(user) + expectNil(t, err) + expectEquals(t, i.PreferredUsername, "12345") + + c.Config.PreferredUsernameField = "name" + i, err = c.identityFromCrowdUser(user) + expectNil(t, err) + expectEquals(t, i.PreferredUsername, "testuser") + + c.Config.PreferredUsernameField = "email" + i, err = c.identityFromCrowdUser(user) + expectNil(t, err) + expectEquals(t, i.PreferredUsername, "testuser@example.com") + + c.Config.PreferredUsernameField = "invalidstring" + i, err = c.identityFromCrowdUser(user) + expectNil(t, err) + expectEquals(t, i.PreferredUsername, "") +} + type TestServerResponse struct { Body interface{} Code int