OAuth: Add optional strict parsing of role_attribute_path (#28021)

* OAuth: Add strict role mapping

By default the user is assigned the role Viewer if role_attribute_path
doesn't return a role, which is not always desirable. This commit adds a
strict mode, which deny the user access if a role isn't returned.

Fix #26626

* Update docs/sources/auth/generic-oauth.md

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>

* Update docs/sources/auth/generic-oauth.md

* Update .gitignore file with WAN

* Removed WAN from .gitignore

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com>
Co-authored-by: achatterjee-grafana <aparajita.chatterjee@grafana.com>
pull/33007/head
Kristian Klausen 4 years ago committed by GitHub
parent 68f38aa49b
commit 4fc0d42470
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      conf/defaults.ini
  2. 2
      conf/sample.ini
  3. 2
      docs/sources/auth/generic-oauth.md
  4. 5
      pkg/login/social/generic_oauth.go
  5. 4
      pkg/login/social/okta_oauth.go
  6. 3
      pkg/login/social/social.go
  7. 1
      pkg/setting/setting_oauth.go

@ -458,6 +458,7 @@ api_url = https://<tenant-id>.okta.com/oauth2/v1/userinfo
allowed_domains =
allowed_groups =
role_attribute_path =
role_attribute_strict = false
#################################### Generic OAuth #######################
[auth.generic_oauth]
@ -472,6 +473,7 @@ email_attribute_path =
login_attribute_path =
name_attribute_path =
role_attribute_path =
role_attribute_strict = false
id_token_attribute_name =
auth_url =
token_url =

@ -449,6 +449,7 @@
;allowed_domains =
;allowed_groups =
;role_attribute_path =
;role_attribute_strict = false
#################################### Generic OAuth ##########################
[auth.generic_oauth]
@ -470,6 +471,7 @@
;team_ids =
;allowed_organizations =
;role_attribute_path =
;role_attribute_strict = false
;tls_skip_verify_insecure = false
;tls_client_cert =
;tls_client_key =

@ -194,6 +194,8 @@ To ease configuration of a proper JMESPath expression, you can test/evaluate exp
### Role mapping
If  the`role_attribute_path` property does not return a role, then the user is assigned the `Viewer` role by default. You can disable the role assignment by setting `role_attribute_strict = true`. It denies user access if no role or an invalid role is returned.
**Basic example:**
In the following example user will get `Editor` as role when authenticating. The value of the property `role` will be the resulting role if the role is a proper Grafana role, i.e. `Viewer`, `Editor` or `Admin`.

@ -26,6 +26,7 @@ type SocialGenericOAuth struct {
loginAttributePath string
nameAttributePath string
roleAttributePath string
roleAttributeStrict bool
idTokenAttributeName string
teamIds []int
}
@ -166,6 +167,10 @@ func (s *SocialGenericOAuth) UserInfo(client *http.Client, token *oauth2.Token)
userInfo.Login = userInfo.Email
}
if s.roleAttributeStrict && !models.RoleType(userInfo.Role).IsValid() {
return nil, errors.New("invalid role")
}
if !s.IsTeamMember(client) {
return nil, errors.New("user not a member of one of the required teams")
}

@ -17,6 +17,7 @@ type SocialOkta struct {
apiUrl string
allowedGroups []string
roleAttributePath string
roleAttributeStrict bool
}
type OktaUserInfoJson struct {
@ -81,6 +82,9 @@ func (s *SocialOkta) UserInfo(client *http.Client, token *oauth2.Token) (*BasicU
if err != nil {
s.log.Error("Failed to extract role", "error", err)
}
if s.roleAttributeStrict && !models.RoleType(role).IsValid() {
return nil, errors.New("invalid role")
}
groups := s.GetGroups(&data)
if !s.IsGroupMember(groups) {

@ -96,6 +96,7 @@ func NewOAuthService() {
EmailAttributeName: sec.Key("email_attribute_name").String(),
EmailAttributePath: sec.Key("email_attribute_path").String(),
RoleAttributePath: sec.Key("role_attribute_path").String(),
RoleAttributeStrict: sec.Key("role_attribute_strict").MustBool(),
AllowedDomains: util.SplitString(sec.Key("allowed_domains").String()),
HostedDomain: sec.Key("hosted_domain").String(),
AllowSignup: sec.Key("allow_sign_up").MustBool(),
@ -171,6 +172,7 @@ func NewOAuthService() {
apiUrl: info.ApiUrl,
allowedGroups: util.SplitString(sec.Key("allowed_groups").String()),
roleAttributePath: info.RoleAttributePath,
roleAttributeStrict: info.RoleAttributeStrict,
}
}
@ -183,6 +185,7 @@ func NewOAuthService() {
emailAttributePath: info.EmailAttributePath,
nameAttributePath: sec.Key("name_attribute_path").String(),
roleAttributePath: info.RoleAttributePath,
roleAttributeStrict: info.RoleAttributeStrict,
loginAttributePath: sec.Key("login_attribute_path").String(),
idTokenAttributeName: sec.Key("id_token_attribute_name").String(),
teamIds: sec.Key("team_ids").Ints(","),

@ -8,6 +8,7 @@ type OAuthInfo struct {
EmailAttributeName string
EmailAttributePath string
RoleAttributePath string
RoleAttributeStrict bool
AllowedDomains []string
HostedDomain string
ApiUrl string

Loading…
Cancel
Save