Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auth: Enforce role sync except if skip org role sync is enabled #70766

Merged
merged 13 commits into from
Jul 17, 2023

Conversation

Jguer
Copy link
Contributor

@Jguer Jguer commented Jun 27, 2023

What is this feature?

If the information provided by the identity provider is empty, make the idP set the default org role.

Use skip_org_role_sync to leave it empty
Use role_attribute_strict to ensure it gets set by the idP

Release notice breaking change

This change impacts GitHub OAuth, Gitlab OAuth, Okta OAuth and Generic OAuth

Currently if no organization role mapping is found for a user when connecting via OAuth, Grafana doesn’t update the user’s organization role.

With Grafana 10.1, on every login, if the role_attribute_path property does not return a role, then the user is assigned the role specified by the auto_assign_org_role option.

To avoid overriding manually set roles, enable the skip_org_role_sync option in the Grafana configuration for your OAuth provider.

@Jguer Jguer added this to the 10.1.x milestone Jun 27, 2023
@Jguer Jguer self-assigned this Jun 27, 2023
@Jguer Jguer requested a review from a team as a code owner June 27, 2023 16:00
@Jguer Jguer requested review from linoman and IevaVasiljeva and removed request for a team June 27, 2023 16:00
@Jguer Jguer requested a review from mgyongyosi June 28, 2023 09:06
Copy link
Contributor

@IevaVasiljeva IevaVasiljeva left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly looks good, but I added some questions about:

  • the behaviour when roleAttributeStrict is set for generic OAuth;
  • the role that we should default to if default org role is not set in the config;
  • the use of errutil.

I think we should also update the description of auto_assign_org_role in the config and docs, as it's currently not very accurate ("# Default role new users will be automatically assigned (if auto_assign_org above is set to true)"). I'm happy to handle docs as part of the docs work, but I think we should update the description in config files now.

Comment on lines +13 to +15
errRoleAttributePathNotSet = errutil.NewBase(errutil.StatusBadRequest,
"oauth.role_attribute_path_not_set",
errutil.WithPublicMessage("Instance role_attribute_path misconfigured, please contact your administrator"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure the errors will be parsed correctly, as we seem to use errutil error where this error is returned: https://github.com/grafana/grafana/blob/main/pkg/services/authn/clients/oauth.go#L125

Maybe it's fine, but worth testing (I can give it a go if you want).

Comment on lines 389 to 390
if role != "" {
return "", false, errInvalidRole.Errorf("invalid role: %s", role)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: looks like we already check that the role is valid when we read the settings: https://github.com/grafana/grafana/blob/main/pkg/setting/setting.go#L1641 But I guess it doesn't hurt to do it twice.

pkg/login/social/social.go Show resolved Hide resolved
}
}
if (userInfo.Role == "" || userInfo.Role == s.defaultRole()) && !s.skipOrgRoleSync {
role, grafanaAdmin, err := s.extractRoleAndAdmin(data.rawJSON, []string{}, true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will fail if we fail to get the role from the token and roleAttributeStrict, right? I think we should allow for the role to be extracted from the API.

@Jguer Jguer requested review from torkelo and a team as code owners July 6, 2023 14:02
@Jguer Jguer requested review from papagian, zserge, suntala, IevaVasiljeva and mgyongyosi and removed request for a team July 6, 2023 14:02
Copy link
Contributor

@linoman linoman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment on lines +385 to +386
} else if role != "" {
return "", false, errInvalidRole.Errorf("invalid role: %s", role)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: should we fail if an invalid role is found? Or just if the role is invalid + roleAttributeStrict is set?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we'll only log and fallthrough to the next method.

After the 2 methods are evaluated we'll set to default role if the role is still "".

Copy link
Contributor

@IevaVasiljeva IevaVasiljeva left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to block this again, but I think that the logic still isn't correct for generic OAuth.


func (s *SocialBase) extractRoleAndAdmin(rawJSON []byte, groups []string) (org.RoleType, bool, error) {
role, gAdmin, err := s.extractRoleAndAdminOptional(rawJSON, groups)
if role == "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: should we still set the role to default even if an error is returned?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed so it's only handled after the 2 methods

Comment on lines +146 to +147
role, grafanaAdmin, err := s.extractRoleAndAdminOptional(data.rawJSON, []string{})
if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't think we can return an error here - this would fail if roleAttributeStrict is set to true, but the role is being sent through the user info endpoint (it would fail, as the role won't be successfully parsed from the token).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed so role attribute strict is only evaluated after the 2 methods

@Jguer Jguer added the breaking change Relevant for changelog generation label Jul 12, 2023
@Jguer Jguer requested a review from IevaVasiljeva July 12, 2023 15:58
@Jguer Jguer added breaking change Relevant for changelog generation and removed breaking change Relevant for changelog generation labels Jul 12, 2023
Copy link
Contributor

@IevaVasiljeva IevaVasiljeva left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me now! Fingers crossed that we haven't missed anything 🤞

Copy link
Contributor

@gamab gamab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, great job! 🙂
I like how tidy the extractRoleAndAdmin code is.

Food for thought: Would it be possible to use extractRoleAndAdmin for AzureAD as well with a default role_attribute_path correctly set? To have all providers falling back on the same function.

@@ -1645,7 +1646,11 @@ func readUserSettings(iniFile *ini.File, cfg *Cfg) error {
AllowUserOrgCreate = users.Key("allow_org_create").MustBool(true)
cfg.AutoAssignOrg = users.Key("auto_assign_org").MustBool(true)
cfg.AutoAssignOrgId = users.Key("auto_assign_org_id").MustInt(1)
cfg.AutoAssignOrgRole = users.Key("auto_assign_org_role").In("Editor", []string{"Editor", "Admin", "Viewer"})
cfg.AutoAssignOrgRole = users.Key("auto_assign_org_role").In(
string(roletype.RoleViewer), []string{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems more logic to be in line with defaults.ini 😄

pkg/login/social/generic_oauth.go Outdated Show resolved Hide resolved
pkg/login/social/gitlab_oauth_test.go Outdated Show resolved Hide resolved
pkg/login/social/gitlab_oauth_test.go Outdated Show resolved Hide resolved
Jguer and others added 5 commits July 13, 2023 13:26
Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
@Jguer Jguer merged commit 0ffd359 into main Jul 17, 2023
12 checks passed
@Jguer Jguer deleted the jguer/remove-legacy-enforce-sync branch July 17, 2023 13:58
linoman pushed a commit that referenced this pull request Jul 24, 2023
* enforce role sync except if skip org role sync is enabled

* move errors to errors file and set codes

* fix docs and defaults

* remove legacy parameter

* support fall through token-api in generic oauth

* fix error handling for generic_oauth

* Update pkg/login/social/generic_oauth.go

Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>

* Update pkg/login/social/gitlab_oauth_test.go

Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>

* Update pkg/login/social/gitlab_oauth_test.go

Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>

---------

Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
@ricky-undeadcoders ricky-undeadcoders modified the milestones: 10.1.x, 10.1.0 Aug 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants