use claim mappings when retrieving user identity

Signed-off-by: Rui Yang <ruiya@vmware.com>
This commit is contained in:
Rui Yang 2020-10-19 22:18:25 -04:00
parent 60b8875780
commit 02860da8b6
3 changed files with 90 additions and 50 deletions

View file

@ -28,10 +28,12 @@ type oauthConnector struct {
authorizationURL string authorizationURL string
userInfoURL string userInfoURL string
scopes []string scopes []string
groupsKey string
userIDKey string userIDKey string
userNameKey string userNameKey string
preferredUsernameKey string preferredUsernameKey string
emailKey string
emailVerifiedKey string
groupsKey string
httpClient *http.Client httpClient *http.Client
logger log.Logger logger log.Logger
} }
@ -41,36 +43,43 @@ type connectorData struct {
} }
type Config struct { type Config struct {
ClientID string `json:"clientID"` ClientID string `json:"clientID"`
ClientSecret string `json:"clientSecret"` ClientSecret string `json:"clientSecret"`
RedirectURI string `json:"redirectURI"` RedirectURI string `json:"redirectURI"`
TokenURL string `json:"tokenURL"` TokenURL string `json:"tokenURL"`
AuthorizationURL string `json:"authorizationURL"` AuthorizationURL string `json:"authorizationURL"`
UserInfoURL string `json:"userInfoURL"` UserInfoURL string `json:"userInfoURL"`
Scopes []string `json:"scopes"` Scopes []string `json:"scopes"`
GroupsKey string `json:"groupsKey"` RootCAs []string `json:"rootCAs"`
UserIDKey string `json:"userIDKey"` InsecureSkipVerify bool `json:"insecureSkipVerify"`
UserNameKey string `json:"userNameKey"` UserIDKey string `json:"userIDKey"` // defaults to "id"
PreferredUsernameKey string `json:"preferredUsernameKey"` ClaimMapping struct {
RootCAs []string `json:"rootCAs"` UserNameKey string `json:"userNameKey"` // defaults to "user_name"
InsecureSkipVerify bool `json:"insecureSkipVerify"` PreferredUsernameKey string `json:"preferredUsernameKey"` // defaults to "preferred_username"
GroupsKey string `json:"groupsKey"` // defaults to "groups"
EmailKey string `json:"emailKey"` // defaults to "email"
EmailVerifiedKey string `json:"emailVerifiedKey"` // defaults to "email_verified"
} `json:"claimMapping"`
} }
func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) { func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
var err error var err error
oauthConn := &oauthConnector{ oauthConn := &oauthConnector{
clientID: c.ClientID, clientID: c.ClientID,
clientSecret: c.ClientSecret, clientSecret: c.ClientSecret,
tokenURL: c.TokenURL, tokenURL: c.TokenURL,
authorizationURL: c.AuthorizationURL, authorizationURL: c.AuthorizationURL,
userInfoURL: c.UserInfoURL, userInfoURL: c.UserInfoURL,
scopes: c.Scopes, scopes: c.Scopes,
groupsKey: c.GroupsKey, redirectURI: c.RedirectURI,
userIDKey: c.UserIDKey, logger: logger,
userNameKey: c.UserNameKey, userIDKey: c.UserIDKey,
redirectURI: c.RedirectURI, userNameKey: c.ClaimMapping.UserNameKey,
logger: logger, preferredUsernameKey: c.ClaimMapping.PreferredUsernameKey,
groupsKey: c.ClaimMapping.GroupsKey,
emailKey: c.ClaimMapping.EmailKey,
emailVerifiedKey: c.ClaimMapping.EmailVerifiedKey,
} }
oauthConn.httpClient, err = newHTTPClient(c.RootCAs, c.InsecureSkipVerify) oauthConn.httpClient, err = newHTTPClient(c.RootCAs, c.InsecureSkipVerify)
@ -173,26 +182,39 @@ func (c *oauthConnector) HandleCallback(s connector.Scopes, r *http.Request) (id
} }
if c.userIDKey == "" { if c.userIDKey == "" {
c.userIDKey = "user_id" c.userIDKey = "id"
} }
userID, found := userInfoResult[c.userIDKey].(string)
if !found {
return identity, fmt.Errorf("OAuth Connector: not found %v claim", c.userIDKey)
}
identity.UserID = userID
if c.userNameKey == "" { if c.userNameKey == "" {
c.userNameKey = "user_name" c.userNameKey = "user_name"
} }
if c.groupsKey == "" {
c.groupsKey = "groups"
}
if c.preferredUsernameKey == "" { if c.preferredUsernameKey == "" {
c.preferredUsernameKey = "preferred_username" c.preferredUsernameKey = "preferred_username"
} }
identity.UserID, _ = userInfoResult[c.userIDKey].(string) if c.groupsKey == "" {
c.groupsKey = "groups"
}
if c.emailKey == "" {
c.emailKey = "email"
}
if c.emailVerifiedKey == "" {
c.emailVerifiedKey = "email_verified"
}
identity.Username, _ = userInfoResult[c.userNameKey].(string) identity.Username, _ = userInfoResult[c.userNameKey].(string)
identity.PreferredUsername, _ = userInfoResult[c.preferredUsernameKey].(string) identity.PreferredUsername, _ = userInfoResult[c.preferredUsernameKey].(string)
identity.Email, _ = userInfoResult["email"].(string) identity.Email, _ = userInfoResult[c.emailKey].(string)
identity.EmailVerified, _ = userInfoResult["email_verified"].(bool) identity.EmailVerified, _ = userInfoResult[c.emailVerifiedKey].(bool)
if s.Groups { if s.Groups {
groups := map[string]bool{} groups := map[string]bool{}

View file

@ -75,8 +75,8 @@ func TestHandleCallBackForGroupsInUserInfo(t *testing.T) {
"user_id_key": "test-user-id", "user_id_key": "test-user-id",
"user_name_key": "test-username", "user_name_key": "test-username",
"preferred_username": "test-preferred-username", "preferred_username": "test-preferred-username",
"email": "test-email", "mail": "mod_mail",
"email_verified": true, "has_verified_email": false,
"groups_key": []string{"admin-group", "user-group"}, "groups_key": []string{"admin-group", "user-group"},
} }
@ -96,8 +96,8 @@ func TestHandleCallBackForGroupsInUserInfo(t *testing.T) {
assert.Equal(t, identity.UserID, "test-user-id") assert.Equal(t, identity.UserID, "test-user-id")
assert.Equal(t, identity.Username, "test-username") assert.Equal(t, identity.Username, "test-username")
assert.Equal(t, identity.PreferredUsername, "test-preferred-username") assert.Equal(t, identity.PreferredUsername, "test-preferred-username")
assert.Equal(t, identity.Email, "test-email") assert.Equal(t, identity.Email, "mod_mail")
assert.Equal(t, identity.EmailVerified, true) assert.Equal(t, identity.EmailVerified, false)
} }
func TestHandleCallBackForGroupsInToken(t *testing.T) { func TestHandleCallBackForGroupsInToken(t *testing.T) {
@ -128,8 +128,8 @@ func TestHandleCallBackForGroupsInToken(t *testing.T) {
assert.Equal(t, identity.PreferredUsername, "test-preferred-username") assert.Equal(t, identity.PreferredUsername, "test-preferred-username")
assert.Equal(t, identity.UserID, "test-user-id") assert.Equal(t, identity.UserID, "test-user-id")
assert.Equal(t, identity.Username, "test-username") assert.Equal(t, identity.Username, "test-username")
assert.Equal(t, identity.Email, "test-email") assert.Equal(t, identity.Email, "")
assert.Equal(t, identity.EmailVerified, true) assert.Equal(t, identity.EmailVerified, false)
} }
func testSetup(t *testing.T, tokenClaims map[string]interface{}, userInfoClaims map[string]interface{}) *httptest.Server { func testSetup(t *testing.T, tokenClaims map[string]interface{}, userInfoClaims map[string]interface{}) *httptest.Server {
@ -198,11 +198,14 @@ func newConnector(t *testing.T, serverURL string) *oauthConnector {
AuthorizationURL: serverURL + "/authorize", AuthorizationURL: serverURL + "/authorize",
UserInfoURL: serverURL + "/userinfo", UserInfoURL: serverURL + "/userinfo",
Scopes: []string{"openid", "groups"}, Scopes: []string{"openid", "groups"},
GroupsKey: "groups_key",
UserIDKey: "user_id_key", UserIDKey: "user_id_key",
UserNameKey: "user_name_key",
} }
testConfig.ClaimMapping.UserNameKey = "user_name_key"
testConfig.ClaimMapping.GroupsKey = "groups_key"
testConfig.ClaimMapping.EmailKey = "mail"
testConfig.ClaimMapping.EmailVerifiedKey = "has_verified_email"
log := logrus.New() log := logrus.New()
conn, err := testConfig.Open("id", log) conn, err := testConfig.Open("id", log)

View file

@ -35,15 +35,30 @@ connectors:
# scopes: # scopes:
# - identity # - identity
# Optional: Configurable keys for groups claim look up # Optional: Configurable keys for user ID look up
# Default: groups # Default: id
# groupsKey:
# Optional: Configurable keys for user ID claim look up
# Default: user_id
# userIDKey: # userIDKey:
# Optional: Configurable keys for preferred username claim look up # Auth roviders return non-standard user identity profile
# Default: preferred_username # Use claimMapping to map those user infomations to standard claims:
# preferredUsernameKey: claimMapping:
# Optional: Configurable keys for user name look up
# Default: user_name
# userNameKey:
# Optional: Configurable keys for preferred username look up
# Default: preferred_username
# preferredUsernameKey:
# Optional: Configurable keys for user groups look up
# Default: groups
# groupsKey:
# Optional: Configurable keys for email look up
# Default: email
# emailKey:
# Optional: Configurable keys for email verified look up
# Default: email_verified
# emailVerifiedKey:
``` ```