diff --git a/conf/defaults.ini b/conf/defaults.ini index 700bbb687b6..1c96618ac42 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -508,6 +508,7 @@ auth_url = https://login.microsoftonline.com//oauth2/v2.0/authorize token_url = https://login.microsoftonline.com//oauth2/v2.0/token allowed_domains = allowed_groups = +role_attribute_strict = false #################################### Okta OAuth ####################### [auth.okta] diff --git a/conf/sample.ini b/conf/sample.ini index 225a6d82bfa..55b3ceed354 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -493,6 +493,7 @@ ;token_url = https://login.microsoftonline.com//oauth2/v2.0/token ;allowed_domains = ;allowed_groups = +;role_attribute_strict = false #################################### Okta OAuth ####################### [auth.okta] diff --git a/docs/sources/auth/azuread.md b/docs/sources/auth/azuread.md index df58e75d4d9..11c14976bba 100644 --- a/docs/sources/auth/azuread.md +++ b/docs/sources/auth/azuread.md @@ -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 allowed_domains = allowed_groups = +role_attribute_strict = false ``` You can also use these environment variables to configure **client_id** and **client_secret**: diff --git a/pkg/login/social/azuread_oauth.go b/pkg/login/social/azuread_oauth.go index 9ce173b65f1..c305e29bb81 100644 --- a/pkg/login/social/azuread_oauth.go +++ b/pkg/login/social/azuread_oauth.go @@ -17,8 +17,9 @@ import ( type SocialAzureAD struct { *SocialBase - allowedGroups []string - autoAssignOrgRole string + allowedGroups []string + autoAssignOrgRole string + roleAttributeStrict bool } 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") } - 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) groups, err := extractGroups(client, claims, token) @@ -118,7 +122,7 @@ func extractEmail(claims azureClaims) string { 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 { 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 } diff --git a/pkg/login/social/azuread_oauth_test.go b/pkg/login/social/azuread_oauth_test.go index b14452809a4..b81c9667978 100644 --- a/pkg/login/social/azuread_oauth_test.go +++ b/pkg/login/social/azuread_oauth_test.go @@ -18,9 +18,10 @@ import ( func TestSocialAzureAD_UserInfo(t *testing.T) { type fields struct { - SocialBase *SocialBase - allowedGroups []string - autoAssignOrgRole string + SocialBase *SocialBase + allowedGroups []string + autoAssignOrgRole string + roleAttributeStrict bool } type args struct { client *http.Client @@ -279,13 +280,30 @@ func TestSocialAzureAD_UserInfo(t *testing.T) { }, 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 { t.Run(tt.name, func(t *testing.T) { s := &SocialAzureAD{ - SocialBase: tt.fields.SocialBase, - allowedGroups: tt.fields.allowedGroups, - autoAssignOrgRole: tt.fields.autoAssignOrgRole, + SocialBase: tt.fields.SocialBase, + allowedGroups: tt.fields.allowedGroups, + autoAssignOrgRole: tt.fields.autoAssignOrgRole, + roleAttributeStrict: tt.fields.roleAttributeStrict, } key := []byte("secret") diff --git a/pkg/login/social/social.go b/pkg/login/social/social.go index aad424e3900..899716b3741 100644 --- a/pkg/login/social/social.go +++ b/pkg/login/social/social.go @@ -149,9 +149,10 @@ func ProvideService(cfg *setting.Cfg) *SocialService { // AzureAD. if name == "azuread" { ss.socialMap["azuread"] = &SocialAzureAD{ - SocialBase: newSocialBase(name, &config, info), - allowedGroups: util.SplitString(sec.Key("allowed_groups").String()), - autoAssignOrgRole: cfg.AutoAssignOrgRole, + SocialBase: newSocialBase(name, &config, info), + allowedGroups: util.SplitString(sec.Key("allowed_groups").String()), + autoAssignOrgRole: cfg.AutoAssignOrgRole, + roleAttributeStrict: info.RoleAttributeStrict, } }