AzureAD OAuth: Add optional strict parsing of role_attribute_path for Azure AD (#42157)

* AzureAD OAuth: Add optional strict parsing of role_attribute_path for Azure AD

Fix casting issues

modify unit tests

Unit test fix

Add proper test args

* Return empty role when using strict attribute mode

* Raise error on empty role

* Fix UT for latest case
This commit is contained in:
sivamu 2022-03-18 05:34:16 -05:00 committed by GitHub
parent ea7d5a6185
commit 6c468daabc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 43 additions and 13 deletions

View File

@ -508,6 +508,7 @@ auth_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize
token_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token token_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token
allowed_domains = allowed_domains =
allowed_groups = allowed_groups =
role_attribute_strict = false
#################################### Okta OAuth ####################### #################################### Okta OAuth #######################
[auth.okta] [auth.okta]

View File

@ -493,6 +493,7 @@
;token_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token ;token_url = https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token
;allowed_domains = ;allowed_domains =
;allowed_groups = ;allowed_groups =
;role_attribute_strict = false
#################################### Okta OAuth ####################### #################################### Okta OAuth #######################
[auth.okta] [auth.okta]

View File

@ -109,6 +109,7 @@ auth_url = https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/authorize
token_url = https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token token_url = https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token
allowed_domains = allowed_domains =
allowed_groups = allowed_groups =
role_attribute_strict = false
``` ```
You can also use these environment variables to configure **client_id** and **client_secret**: You can also use these environment variables to configure **client_id** and **client_secret**:

View File

@ -17,8 +17,9 @@ import (
type SocialAzureAD struct { type SocialAzureAD struct {
*SocialBase *SocialBase
allowedGroups []string allowedGroups []string
autoAssignOrgRole string autoAssignOrgRole string
roleAttributeStrict bool
} }
type azureClaims struct { type azureClaims struct {
@ -69,7 +70,10 @@ func (s *SocialAzureAD) UserInfo(client *http.Client, token *oauth2.Token) (*Bas
return nil, errors.New("error getting user info: no email found in access token") return nil, errors.New("error getting user info: no email found in access token")
} }
role := extractRole(claims, s.autoAssignOrgRole) role := extractRole(claims, s.autoAssignOrgRole, s.roleAttributeStrict)
if role == "" {
return nil, errors.New("user does not have a valid role")
}
logger.Debug("AzureAD OAuth: extracted role", "email", email, "role", role) logger.Debug("AzureAD OAuth: extracted role", "email", email, "role", role)
groups, err := extractGroups(client, claims, token) groups, err := extractGroups(client, claims, token)
@ -118,7 +122,7 @@ func extractEmail(claims azureClaims) string {
return claims.Email return claims.Email
} }
func extractRole(claims azureClaims, autoAssignRole string) models.RoleType { func extractRole(claims azureClaims, autoAssignRole string, strictMode bool) models.RoleType {
if len(claims.Roles) == 0 { if len(claims.Roles) == 0 {
return models.RoleType(autoAssignRole) return models.RoleType(autoAssignRole)
} }
@ -135,6 +139,10 @@ func extractRole(claims azureClaims, autoAssignRole string) models.RoleType {
} }
} }
if strictMode {
return models.RoleType("")
}
return models.ROLE_VIEWER return models.ROLE_VIEWER
} }

View File

@ -18,9 +18,10 @@ import (
func TestSocialAzureAD_UserInfo(t *testing.T) { func TestSocialAzureAD_UserInfo(t *testing.T) {
type fields struct { type fields struct {
SocialBase *SocialBase SocialBase *SocialBase
allowedGroups []string allowedGroups []string
autoAssignOrgRole string autoAssignOrgRole string
roleAttributeStrict bool
} }
type args struct { type args struct {
client *http.Client client *http.Client
@ -279,13 +280,30 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
}, },
wantErr: false, wantErr: false,
}, },
{
name: "Fetch empty role when strict attribute role is true and no match",
fields: fields{
roleAttributeStrict: true,
},
claims: &azureClaims{
Email: "me@example.com",
PreferredUsername: "",
Roles: []string{"foo"},
Groups: []string{},
Name: "My Name",
ID: "1234",
},
want: nil,
wantErr: true,
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
s := &SocialAzureAD{ s := &SocialAzureAD{
SocialBase: tt.fields.SocialBase, SocialBase: tt.fields.SocialBase,
allowedGroups: tt.fields.allowedGroups, allowedGroups: tt.fields.allowedGroups,
autoAssignOrgRole: tt.fields.autoAssignOrgRole, autoAssignOrgRole: tt.fields.autoAssignOrgRole,
roleAttributeStrict: tt.fields.roleAttributeStrict,
} }
key := []byte("secret") key := []byte("secret")

View File

@ -149,9 +149,10 @@ func ProvideService(cfg *setting.Cfg) *SocialService {
// AzureAD. // AzureAD.
if name == "azuread" { if name == "azuread" {
ss.socialMap["azuread"] = &SocialAzureAD{ ss.socialMap["azuread"] = &SocialAzureAD{
SocialBase: newSocialBase(name, &config, info), SocialBase: newSocialBase(name, &config, info),
allowedGroups: util.SplitString(sec.Key("allowed_groups").String()), allowedGroups: util.SplitString(sec.Key("allowed_groups").String()),
autoAssignOrgRole: cfg.AutoAssignOrgRole, autoAssignOrgRole: cfg.AutoAssignOrgRole,
roleAttributeStrict: info.RoleAttributeStrict,
} }
} }