Chore: Cleanup SocialBase + connectors and use the OAuthInfo (#79453)

* Clean up SocialBase

* Cleanup other connectors
This commit is contained in:
Misi 2023-12-15 12:09:17 +01:00 committed by GitHub
parent af8d8f29f3
commit db81216240
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 76 additions and 107 deletions

View File

@ -143,11 +143,11 @@ func (s *SocialAzureAD) UserInfo(ctx context.Context, client *http.Client, token
} }
var isGrafanaAdmin *bool = nil var isGrafanaAdmin *bool = nil
if s.allowAssignGrafanaAdmin { if s.info.AllowAssignGrafanaAdmin {
isGrafanaAdmin = &grafanaAdmin isGrafanaAdmin = &grafanaAdmin
} }
if s.allowAssignGrafanaAdmin && s.info.SkipOrgRoleSync { if s.info.AllowAssignGrafanaAdmin && s.info.SkipOrgRoleSync {
s.log.Debug("AllowAssignGrafanaAdmin and skipOrgRoleSync are both set, Grafana Admin role will not be synced, consider setting one or the other") s.log.Debug("AllowAssignGrafanaAdmin and skipOrgRoleSync are both set, Grafana Admin role will not be synced, consider setting one or the other")
} }
@ -246,7 +246,7 @@ func (claims *azureClaims) extractEmail() string {
// extractRoleAndAdmin extracts the role from the claims and returns the role and whether the user is a Grafana admin. // extractRoleAndAdmin extracts the role from the claims and returns the role and whether the user is a Grafana admin.
func (s *SocialAzureAD) extractRoleAndAdmin(claims *azureClaims) (org.RoleType, bool, error) { func (s *SocialAzureAD) extractRoleAndAdmin(claims *azureClaims) (org.RoleType, bool, error) {
if len(claims.Roles) == 0 { if len(claims.Roles) == 0 {
if s.roleAttributeStrict { if s.info.RoleAttributeStrict {
return "", false, errRoleAttributeStrictViolation.Errorf("AzureAD OAuth: unset role") return "", false, errRoleAttributeStrictViolation.Errorf("AzureAD OAuth: unset role")
} }
return s.defaultRole(), false, nil return s.defaultRole(), false, nil
@ -264,7 +264,7 @@ func (s *SocialAzureAD) extractRoleAndAdmin(claims *azureClaims) (org.RoleType,
} }
} }
if s.roleAttributeStrict { if s.info.RoleAttributeStrict {
return "", false, errRoleAttributeStrictViolation.Errorf("AzureAD OAuth: idP did not return a valid role %q", claims.Roles) return "", false, errRoleAttributeStrictViolation.Errorf("AzureAD OAuth: idP did not return a valid role %q", claims.Roles)
} }
@ -390,7 +390,7 @@ func (s *SocialAzureAD) groupsGraphAPIURL(claims *azureClaims, token *oauth2.Tok
func (s *SocialAzureAD) SupportBundleContent(bf *bytes.Buffer) error { func (s *SocialAzureAD) SupportBundleContent(bf *bytes.Buffer) error {
bf.WriteString("## AzureAD specific configuration\n\n") bf.WriteString("## AzureAD specific configuration\n\n")
bf.WriteString("```ini\n") bf.WriteString("```ini\n")
bf.WriteString(fmt.Sprintf("allowed_groups = %v\n", s.allowedGroups)) bf.WriteString(fmt.Sprintf("allowed_groups = %v\n", s.info.AllowedGroups))
bf.WriteString(fmt.Sprintf("forceUseGraphAPI = %v\n", s.forceUseGraphAPI)) bf.WriteString(fmt.Sprintf("forceUseGraphAPI = %v\n", s.forceUseGraphAPI))
bf.WriteString("```\n\n") bf.WriteString("```\n\n")

View File

@ -38,11 +38,11 @@ type httpGetResponse struct {
} }
func (s *SocialBase) IsEmailAllowed(email string) bool { func (s *SocialBase) IsEmailAllowed(email string) bool {
return isEmailAllowed(email, s.allowedDomains) return isEmailAllowed(email, s.info.AllowedDomains)
} }
func (s *SocialBase) IsSignupAllowed() bool { func (s *SocialBase) IsSignupAllowed() bool {
return s.allowSignup return s.info.AllowSignup
} }
func isEmailAllowed(email string, allowedDomains []string) bool { func isEmailAllowed(email string, allowedDomains []string) bool {

View File

@ -34,7 +34,6 @@ var _ ssosettings.Reloadable = (*SocialGenericOAuth)(nil)
type SocialGenericOAuth struct { type SocialGenericOAuth struct {
*SocialBase *SocialBase
allowedOrganizations []string allowedOrganizations []string
apiUrl string
teamsUrl string teamsUrl string
emailAttributeName string emailAttributeName string
emailAttributePath string emailAttributePath string
@ -44,14 +43,12 @@ type SocialGenericOAuth struct {
idTokenAttributeName string idTokenAttributeName string
teamIdsAttributePath string teamIdsAttributePath string
teamIds []string teamIds []string
allowedGroups []string
} }
func NewGenericOAuthProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features *featuremgmt.FeatureManager) *SocialGenericOAuth { func NewGenericOAuthProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features *featuremgmt.FeatureManager) *SocialGenericOAuth {
config := createOAuthConfig(info, cfg, social.GenericOAuthProviderName) config := createOAuthConfig(info, cfg, social.GenericOAuthProviderName)
provider := &SocialGenericOAuth{ provider := &SocialGenericOAuth{
SocialBase: newSocialBase(social.GenericOAuthProviderName, config, info, cfg.AutoAssignOrgRole, *features), SocialBase: newSocialBase(social.GenericOAuthProviderName, config, info, cfg.AutoAssignOrgRole, *features),
apiUrl: info.ApiUrl,
teamsUrl: info.TeamsUrl, teamsUrl: info.TeamsUrl,
emailAttributeName: info.EmailAttributeName, emailAttributeName: info.EmailAttributeName,
emailAttributePath: info.EmailAttributePath, emailAttributePath: info.EmailAttributePath,
@ -62,7 +59,6 @@ func NewGenericOAuthProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettin
teamIdsAttributePath: info.TeamIdsAttributePath, teamIdsAttributePath: info.TeamIdsAttributePath,
teamIds: util.SplitString(info.Extra[teamIdsKey]), teamIds: util.SplitString(info.Extra[teamIdsKey]),
allowedOrganizations: util.SplitString(info.Extra[allowedOrganizationsKey]), allowedOrganizations: util.SplitString(info.Extra[allowedOrganizationsKey]),
allowedGroups: info.AllowedGroups,
} }
if features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsApi) { if features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsApi) {
@ -82,11 +78,11 @@ func (s *SocialGenericOAuth) Reload(ctx context.Context, settings ssoModels.SSOS
// TODOD: remove this in the next PR and use the isGroupMember from social.go // TODOD: remove this in the next PR and use the isGroupMember from social.go
func (s *SocialGenericOAuth) IsGroupMember(groups []string) bool { func (s *SocialGenericOAuth) IsGroupMember(groups []string) bool {
if len(s.allowedGroups) == 0 { if len(s.info.AllowedGroups) == 0 {
return true return true
} }
for _, allowedGroup := range s.allowedGroups { for _, allowedGroup := range s.info.AllowedGroups {
for _, group := range groups { for _, group := range groups {
if group == allowedGroup { if group == allowedGroup {
return true return true
@ -198,7 +194,7 @@ func (s *SocialGenericOAuth) UserInfo(ctx context.Context, client *http.Client,
s.log.Warn("Failed to extract role", "err", err) s.log.Warn("Failed to extract role", "err", err)
} else { } else {
userInfo.Role = role userInfo.Role = role
if s.allowAssignGrafanaAdmin { if s.info.AllowAssignGrafanaAdmin {
userInfo.IsGrafanaAdmin = &grafanaAdmin userInfo.IsGrafanaAdmin = &grafanaAdmin
} }
} }
@ -216,13 +212,13 @@ func (s *SocialGenericOAuth) UserInfo(ctx context.Context, client *http.Client,
} }
if userInfo.Role == "" && !s.info.SkipOrgRoleSync { if userInfo.Role == "" && !s.info.SkipOrgRoleSync {
if s.roleAttributeStrict { if s.info.RoleAttributeStrict {
return nil, errRoleAttributeStrictViolation.Errorf("idP did not return a role attribute") return nil, errRoleAttributeStrictViolation.Errorf("idP did not return a role attribute")
} }
userInfo.Role = s.defaultRole() userInfo.Role = s.defaultRole()
} }
if s.allowAssignGrafanaAdmin && s.info.SkipOrgRoleSync { if s.info.AllowAssignGrafanaAdmin && s.info.SkipOrgRoleSync {
s.log.Debug("AllowAssignGrafanaAdmin and skipOrgRoleSync are both set, Grafana Admin role will not be synced, consider setting one or the other") s.log.Debug("AllowAssignGrafanaAdmin and skipOrgRoleSync are both set, Grafana Admin role will not be synced, consider setting one or the other")
} }
@ -295,14 +291,14 @@ func (s *SocialGenericOAuth) extractFromToken(token *oauth2.Token) *UserInfoJson
func (s *SocialGenericOAuth) extractFromAPI(ctx context.Context, client *http.Client) *UserInfoJson { func (s *SocialGenericOAuth) extractFromAPI(ctx context.Context, client *http.Client) *UserInfoJson {
s.log.Debug("Getting user info from API") s.log.Debug("Getting user info from API")
if s.apiUrl == "" { if s.info.ApiUrl == "" {
s.log.Debug("No api url configured") s.log.Debug("No api url configured")
return nil return nil
} }
rawUserInfoResponse, err := s.httpGet(ctx, client, s.apiUrl) rawUserInfoResponse, err := s.httpGet(ctx, client, s.info.ApiUrl)
if err != nil { if err != nil {
s.log.Debug("Error getting user info from API", "url", s.apiUrl, "error", err) s.log.Debug("Error getting user info from API", "url", s.info.ApiUrl, "error", err)
return nil return nil
} }
@ -418,9 +414,9 @@ func (s *SocialGenericOAuth) FetchPrivateEmail(ctx context.Context, client *http
IsConfirmed bool `json:"is_confirmed"` IsConfirmed bool `json:"is_confirmed"`
} }
response, err := s.httpGet(ctx, client, fmt.Sprintf(s.apiUrl+"/emails")) response, err := s.httpGet(ctx, client, fmt.Sprintf(s.info.ApiUrl+"/emails"))
if err != nil { if err != nil {
s.log.Error("Error getting email address", "url", s.apiUrl+"/emails", "error", err) s.log.Error("Error getting email address", "url", s.info.ApiUrl+"/emails", "error", err)
return "", fmt.Errorf("%v: %w", "Error getting email address", err) return "", fmt.Errorf("%v: %w", "Error getting email address", err)
} }
@ -480,9 +476,9 @@ func (s *SocialGenericOAuth) fetchTeamMembershipsFromDeprecatedTeamsUrl(ctx cont
Id int `json:"id"` Id int `json:"id"`
} }
response, err := s.httpGet(ctx, client, fmt.Sprintf(s.apiUrl+"/teams")) response, err := s.httpGet(ctx, client, fmt.Sprintf(s.info.ApiUrl+"/teams"))
if err != nil { if err != nil {
s.log.Error("Error getting team memberships", "url", s.apiUrl+"/teams", "error", err) s.log.Error("Error getting team memberships", "url", s.info.ApiUrl+"/teams", "error", err)
return []string{}, err return []string{}, err
} }
@ -521,9 +517,9 @@ func (s *SocialGenericOAuth) FetchOrganizations(ctx context.Context, client *htt
Login string `json:"login"` Login string `json:"login"`
} }
response, err := s.httpGet(ctx, client, fmt.Sprintf(s.apiUrl+"/orgs")) response, err := s.httpGet(ctx, client, fmt.Sprintf(s.info.ApiUrl+"/orgs"))
if err != nil { if err != nil {
s.log.Error("Error getting organizations", "url", s.apiUrl+"/orgs", "error", err) s.log.Error("Error getting organizations", "url", s.info.ApiUrl+"/orgs", "error", err)
return nil, false return nil, false
} }

View File

@ -207,7 +207,7 @@ func TestSearchJSONForRole(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
provider.roleAttributePath = test.RoleAttributePath provider.info.RoleAttributePath = test.RoleAttributePath
t.Run(test.Name, func(t *testing.T) { t.Run(test.Name, func(t *testing.T) {
actualResult, err := provider.searchJSONForStringAttr(test.RoleAttributePath, test.UserInfoJSONResponse) actualResult, err := provider.searchJSONForStringAttr(test.RoleAttributePath, test.UserInfoJSONResponse)
if test.ExpectedError == "" { if test.ExpectedError == "" {
@ -453,8 +453,8 @@ func TestUserInfoSearchesForEmailAndRole(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
provider.roleAttributePath = test.RoleAttributePath provider.info.RoleAttributePath = test.RoleAttributePath
provider.allowAssignGrafanaAdmin = test.AllowAssignGrafanaAdmin provider.info.AllowAssignGrafanaAdmin = test.AllowAssignGrafanaAdmin
provider.info.SkipOrgRoleSync = test.SkipOrgRoleSync provider.info.SkipOrgRoleSync = test.SkipOrgRoleSync
t.Run(test.Name, func(t *testing.T) { t.Run(test.Name, func(t *testing.T) {
@ -466,7 +466,7 @@ func TestUserInfoSearchesForEmailAndRole(t *testing.T) {
_, err = w.Write(body) _, err = w.Write(body)
require.NoError(t, err) require.NoError(t, err)
})) }))
provider.apiUrl = ts.URL provider.info.ApiUrl = ts.URL
staticToken := oauth2.Token{ staticToken := oauth2.Token{
AccessToken: "", AccessToken: "",
TokenType: "", TokenType: "",
@ -566,7 +566,7 @@ func TestUserInfoSearchesForLogin(t *testing.T) {
_, err = w.Write(body) _, err = w.Write(body)
require.NoError(t, err) require.NoError(t, err)
})) }))
provider.apiUrl = ts.URL provider.info.ApiUrl = ts.URL
staticToken := oauth2.Token{ staticToken := oauth2.Token{
AccessToken: "", AccessToken: "",
TokenType: "", TokenType: "",
@ -663,7 +663,7 @@ func TestUserInfoSearchesForName(t *testing.T) {
_, err = w.Write(body) _, err = w.Write(body)
require.NoError(t, err) require.NoError(t, err)
})) }))
provider.apiUrl = ts.URL provider.info.ApiUrl = ts.URL
staticToken := oauth2.Token{ staticToken := oauth2.Token{
AccessToken: "", AccessToken: "",
TokenType: "", TokenType: "",

View File

@ -30,7 +30,6 @@ var _ ssosettings.Reloadable = (*SocialGithub)(nil)
type SocialGithub struct { type SocialGithub struct {
*SocialBase *SocialBase
allowedOrganizations []string allowedOrganizations []string
apiUrl string
teamIds []int teamIds []int
} }
@ -61,7 +60,6 @@ func NewGitHubProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings sso
config := createOAuthConfig(info, cfg, social.GitHubProviderName) config := createOAuthConfig(info, cfg, social.GitHubProviderName)
provider := &SocialGithub{ provider := &SocialGithub{
SocialBase: newSocialBase(social.GitHubProviderName, config, info, cfg.AutoAssignOrgRole, *features), SocialBase: newSocialBase(social.GitHubProviderName, config, info, cfg.AutoAssignOrgRole, *features),
apiUrl: info.ApiUrl,
teamIds: teamIds, teamIds: teamIds,
allowedOrganizations: util.SplitString(info.Extra[allowedOrganizationsKey]), allowedOrganizations: util.SplitString(info.Extra[allowedOrganizationsKey]),
} }
@ -135,7 +133,7 @@ func (s *SocialGithub) FetchPrivateEmail(ctx context.Context, client *http.Clien
Verified bool `json:"verified"` Verified bool `json:"verified"`
} }
response, err := s.httpGet(ctx, client, fmt.Sprintf(s.apiUrl+"/emails")) response, err := s.httpGet(ctx, client, fmt.Sprintf(s.info.ApiUrl+"/emails"))
if err != nil { if err != nil {
return "", fmt.Errorf("Error getting email address: %s", err) return "", fmt.Errorf("Error getting email address: %s", err)
} }
@ -158,7 +156,7 @@ func (s *SocialGithub) FetchPrivateEmail(ctx context.Context, client *http.Clien
} }
func (s *SocialGithub) FetchTeamMemberships(ctx context.Context, client *http.Client) ([]GithubTeam, error) { func (s *SocialGithub) FetchTeamMemberships(ctx context.Context, client *http.Client) ([]GithubTeam, error) {
url := fmt.Sprintf(s.apiUrl + "/teams?per_page=100") url := fmt.Sprintf(s.info.ApiUrl + "/teams?per_page=100")
hasMore := true hasMore := true
teams := make([]GithubTeam, 0) teams := make([]GithubTeam, 0)
@ -240,7 +238,7 @@ func (s *SocialGithub) UserInfo(ctx context.Context, client *http.Client, token
Name string `json:"name"` Name string `json:"name"`
} }
response, err := s.httpGet(ctx, client, s.apiUrl) response, err := s.httpGet(ctx, client, s.info.ApiUrl)
if err != nil { if err != nil {
return nil, fmt.Errorf("error getting user info: %s", err) return nil, fmt.Errorf("error getting user info: %s", err)
} }
@ -266,13 +264,13 @@ func (s *SocialGithub) UserInfo(ctx context.Context, client *http.Client, token
return nil, err return nil, err
} }
if s.allowAssignGrafanaAdmin { if s.info.AllowAssignGrafanaAdmin {
isGrafanaAdmin = &grafanaAdmin isGrafanaAdmin = &grafanaAdmin
} }
} }
// we skip allowing assignment of GrafanaAdmin if skipOrgRoleSync is present // we skip allowing assignment of GrafanaAdmin if skipOrgRoleSync is present
if s.allowAssignGrafanaAdmin && s.info.SkipOrgRoleSync { if s.info.AllowAssignGrafanaAdmin && s.info.SkipOrgRoleSync {
s.log.Debug("AllowAssignGrafanaAdmin and skipOrgRoleSync are both set, Grafana Admin role will not be synced, consider setting one or the other") s.log.Debug("AllowAssignGrafanaAdmin and skipOrgRoleSync are both set, Grafana Admin role will not be synced, consider setting one or the other")
} }
@ -289,7 +287,7 @@ func (s *SocialGithub) UserInfo(ctx context.Context, client *http.Client, token
userInfo.Name = data.Name userInfo.Name = data.Name
} }
organizationsUrl := fmt.Sprintf(s.apiUrl + "/orgs?per_page=100") organizationsUrl := fmt.Sprintf(s.info.ApiUrl + "/orgs?per_page=100")
if !s.IsTeamMember(ctx, client) { if !s.IsTeamMember(ctx, client) {
return nil, ErrMissingTeamMembership.Errorf("User is not a member of any of the allowed teams: %v", s.teamIds) return nil, ErrMissingTeamMembership.Errorf("User is not a member of any of the allowed teams: %v", s.teamIds)

View File

@ -29,7 +29,6 @@ var _ ssosettings.Reloadable = (*SocialGitlab)(nil)
type SocialGitlab struct { type SocialGitlab struct {
*SocialBase *SocialBase
apiUrl string
} }
type apiData struct { type apiData struct {
@ -57,7 +56,6 @@ func NewGitLabProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings sso
config := createOAuthConfig(info, cfg, social.GitlabProviderName) config := createOAuthConfig(info, cfg, social.GitlabProviderName)
provider := &SocialGitlab{ provider := &SocialGitlab{
SocialBase: newSocialBase(social.GitlabProviderName, config, info, cfg.AutoAssignOrgRole, *features), SocialBase: newSocialBase(social.GitlabProviderName, config, info, cfg.AutoAssignOrgRole, *features),
apiUrl: info.ApiUrl,
} }
if features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsApi) { if features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsApi) {
@ -94,7 +92,7 @@ func (s *SocialGitlab) getGroupsPage(ctx context.Context, client *http.Client, n
FullPath string `json:"full_path"` FullPath string `json:"full_path"`
} }
groupURL, err := url.JoinPath(s.apiUrl, "/groups") groupURL, err := url.JoinPath(s.info.ApiUrl, "/groups")
if err != nil { if err != nil {
s.log.Error("Error joining GitLab API URL", "err", err) s.log.Error("Error joining GitLab API URL", "err", err)
return nil, nil return nil, nil
@ -183,7 +181,7 @@ func (s *SocialGitlab) UserInfo(ctx context.Context, client *http.Client, token
return nil, errMissingGroupMembership return nil, errMissingGroupMembership
} }
if s.allowAssignGrafanaAdmin && s.info.SkipOrgRoleSync { if s.info.AllowAssignGrafanaAdmin && s.info.SkipOrgRoleSync {
s.log.Debug("AllowAssignGrafanaAdmin and skipOrgRoleSync are both set, Grafana Admin role will not be synced, consider setting one or the other") s.log.Debug("AllowAssignGrafanaAdmin and skipOrgRoleSync are both set, Grafana Admin role will not be synced, consider setting one or the other")
} }
@ -196,7 +194,7 @@ func (s *SocialGitlab) GetOAuthInfo() *social.OAuthInfo {
func (s *SocialGitlab) extractFromAPI(ctx context.Context, client *http.Client, token *oauth2.Token) (*userData, error) { func (s *SocialGitlab) extractFromAPI(ctx context.Context, client *http.Client, token *oauth2.Token) (*userData, error) {
apiResp := &apiData{} apiResp := &apiData{}
response, err := s.httpGet(ctx, client, s.apiUrl+"/user") response, err := s.httpGet(ctx, client, s.info.ApiUrl+"/user")
if err != nil { if err != nil {
return nil, fmt.Errorf("Error getting user info: %w", err) return nil, fmt.Errorf("Error getting user info: %w", err)
} }
@ -229,7 +227,7 @@ func (s *SocialGitlab) extractFromAPI(ctx context.Context, client *http.Client,
return nil, err return nil, err
} }
if s.allowAssignGrafanaAdmin { if s.info.AllowAssignGrafanaAdmin {
idData.IsGrafanaAdmin = &grafanaAdmin idData.IsGrafanaAdmin = &grafanaAdmin
} }
@ -285,7 +283,7 @@ func (s *SocialGitlab) extractFromToken(ctx context.Context, client *http.Client
return nil, errRole return nil, errRole
} }
if s.allowAssignGrafanaAdmin { if s.info.AllowAssignGrafanaAdmin {
data.IsGrafanaAdmin = &grafanaAdmin data.IsGrafanaAdmin = &grafanaAdmin
} }

View File

@ -161,10 +161,10 @@ func TestSocialGitlab_UserInfo(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
provider.roleAttributePath = test.RoleAttributePath provider.info.RoleAttributePath = test.RoleAttributePath
provider.allowAssignGrafanaAdmin = test.Cfg.AllowAssignGrafanaAdmin provider.info.AllowAssignGrafanaAdmin = test.Cfg.AllowAssignGrafanaAdmin
provider.autoAssignOrgRole = string(test.Cfg.AutoAssignOrgRole) provider.autoAssignOrgRole = string(test.Cfg.AutoAssignOrgRole)
provider.roleAttributeStrict = test.Cfg.RoleAttributeStrict provider.info.RoleAttributeStrict = test.Cfg.RoleAttributeStrict
provider.info.SkipOrgRoleSync = test.Cfg.SkipOrgRoleSync provider.info.SkipOrgRoleSync = test.Cfg.SkipOrgRoleSync
t.Run(test.Name, func(t *testing.T) { t.Run(test.Name, func(t *testing.T) {
@ -187,7 +187,7 @@ func TestSocialGitlab_UserInfo(t *testing.T) {
require.Fail(t, "unexpected request URI: "+r.RequestURI) require.Fail(t, "unexpected request URI: "+r.RequestURI)
} }
})) }))
provider.apiUrl = ts.URL + apiURI provider.info.ApiUrl = ts.URL + apiURI
actualResult, err := provider.UserInfo(context.Background(), ts.Client(), &oauth2.Token{}) actualResult, err := provider.UserInfo(context.Background(), ts.Client(), &oauth2.Token{})
if test.ExpectedError != nil { if test.ExpectedError != nil {
require.ErrorIs(t, err, test.ExpectedError) require.ErrorIs(t, err, test.ExpectedError)

View File

@ -28,8 +28,6 @@ var _ ssosettings.Reloadable = (*SocialGoogle)(nil)
type SocialGoogle struct { type SocialGoogle struct {
*SocialBase *SocialBase
hostedDomain string
apiUrl string
} }
type googleUserData struct { type googleUserData struct {
@ -43,9 +41,7 @@ type googleUserData struct {
func NewGoogleProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features *featuremgmt.FeatureManager) *SocialGoogle { func NewGoogleProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features *featuremgmt.FeatureManager) *SocialGoogle {
config := createOAuthConfig(info, cfg, social.GoogleProviderName) config := createOAuthConfig(info, cfg, social.GoogleProviderName)
provider := &SocialGoogle{ provider := &SocialGoogle{
SocialBase: newSocialBase(social.GoogleProviderName, config, info, cfg.AutoAssignOrgRole, *features), SocialBase: newSocialBase(social.GoogleProviderName, config, info, cfg.AutoAssignOrgRole, *features),
hostedDomain: info.HostedDomain,
apiUrl: info.ApiUrl,
} }
if strings.HasPrefix(info.ApiUrl, legacyAPIURL) { if strings.HasPrefix(info.ApiUrl, legacyAPIURL) {
@ -114,7 +110,7 @@ func (s *SocialGoogle) UserInfo(ctx context.Context, client *http.Client, token
return nil, errRole return nil, errRole
} }
if s.allowAssignGrafanaAdmin { if s.info.AllowAssignGrafanaAdmin {
userInfo.IsGrafanaAdmin = &grafanaAdmin userInfo.IsGrafanaAdmin = &grafanaAdmin
} }
@ -138,9 +134,9 @@ type googleAPIData struct {
} }
func (s *SocialGoogle) extractFromAPI(ctx context.Context, client *http.Client) (*googleUserData, error) { func (s *SocialGoogle) extractFromAPI(ctx context.Context, client *http.Client) (*googleUserData, error) {
if strings.HasPrefix(s.apiUrl, legacyAPIURL) { if strings.HasPrefix(s.info.ApiUrl, legacyAPIURL) {
data := googleAPIData{} data := googleAPIData{}
response, err := s.httpGet(ctx, client, s.apiUrl) response, err := s.httpGet(ctx, client, s.info.ApiUrl)
if err != nil { if err != nil {
return nil, fmt.Errorf("error retrieving legacy user info: %s", err) return nil, fmt.Errorf("error retrieving legacy user info: %s", err)
} }
@ -159,7 +155,7 @@ func (s *SocialGoogle) extractFromAPI(ctx context.Context, client *http.Client)
} }
data := googleUserData{} data := googleUserData{}
response, err := s.httpGet(ctx, client, s.apiUrl) response, err := s.httpGet(ctx, client, s.info.ApiUrl)
if err != nil { if err != nil {
return nil, fmt.Errorf("error getting user info: %s", err) return nil, fmt.Errorf("error getting user info: %s", err)
} }
@ -172,7 +168,7 @@ func (s *SocialGoogle) extractFromAPI(ctx context.Context, client *http.Client)
} }
func (s *SocialGoogle) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string { func (s *SocialGoogle) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string {
if s.features.IsEnabledGlobally(featuremgmt.FlagAccessTokenExpirationCheck) && s.useRefreshToken { if s.features.IsEnabledGlobally(featuremgmt.FlagAccessTokenExpirationCheck) && s.info.UseRefreshToken {
opts = append(opts, oauth2.AccessTypeOffline, oauth2.ApprovalForce) opts = append(opts, oauth2.AccessTypeOffline, oauth2.ApprovalForce)
} }
return s.SocialBase.AuthCodeURL(state, opts...) return s.SocialBase.AuthCodeURL(state, opts...)

View File

@ -23,8 +23,6 @@ var _ ssosettings.Reloadable = (*SocialOkta)(nil)
type SocialOkta struct { type SocialOkta struct {
*SocialBase *SocialBase
apiUrl string
allowedGroups []string
} }
type OktaUserInfoJson struct { type OktaUserInfoJson struct {
@ -49,9 +47,7 @@ type OktaClaims struct {
func NewOktaProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features *featuremgmt.FeatureManager) *SocialOkta { func NewOktaProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features *featuremgmt.FeatureManager) *SocialOkta {
config := createOAuthConfig(info, cfg, social.OktaProviderName) config := createOAuthConfig(info, cfg, social.OktaProviderName)
provider := &SocialOkta{ provider := &SocialOkta{
SocialBase: newSocialBase(social.OktaProviderName, config, info, cfg.AutoAssignOrgRole, *features), SocialBase: newSocialBase(social.OktaProviderName, config, info, cfg.AutoAssignOrgRole, *features),
apiUrl: info.ApiUrl,
allowedGroups: info.AllowedGroups,
} }
if info.UseRefreshToken && features.IsEnabledGlobally(featuremgmt.FlagAccessTokenExpirationCheck) { if info.UseRefreshToken && features.IsEnabledGlobally(featuremgmt.FlagAccessTokenExpirationCheck) {
@ -122,11 +118,11 @@ func (s *SocialOkta) UserInfo(ctx context.Context, client *http.Client, token *o
return nil, err return nil, err
} }
if s.allowAssignGrafanaAdmin { if s.info.AllowAssignGrafanaAdmin {
isGrafanaAdmin = &grafanaAdmin isGrafanaAdmin = &grafanaAdmin
} }
} }
if s.allowAssignGrafanaAdmin && s.info.SkipOrgRoleSync { if s.info.AllowAssignGrafanaAdmin && s.info.SkipOrgRoleSync {
s.log.Debug("AllowAssignGrafanaAdmin and skipOrgRoleSync are both set, Grafana Admin role will not be synced, consider setting one or the other") s.log.Debug("AllowAssignGrafanaAdmin and skipOrgRoleSync are both set, Grafana Admin role will not be synced, consider setting one or the other")
} }
@ -146,9 +142,9 @@ func (s *SocialOkta) GetOAuthInfo() *social.OAuthInfo {
} }
func (s *SocialOkta) extractAPI(ctx context.Context, data *OktaUserInfoJson, client *http.Client) error { func (s *SocialOkta) extractAPI(ctx context.Context, data *OktaUserInfoJson, client *http.Client) error {
rawUserInfoResponse, err := s.httpGet(ctx, client, s.apiUrl) rawUserInfoResponse, err := s.httpGet(ctx, client, s.info.ApiUrl)
if err != nil { if err != nil {
s.log.Debug("Error getting user info response", "url", s.apiUrl, "error", err) s.log.Debug("Error getting user info response", "url", s.info.ApiUrl, "error", err)
return fmt.Errorf("error getting user info response: %w", err) return fmt.Errorf("error getting user info response: %w", err)
} }
data.rawJSON = rawUserInfoResponse.Body data.rawJSON = rawUserInfoResponse.Body
@ -174,11 +170,11 @@ func (s *SocialOkta) GetGroups(data *OktaUserInfoJson) []string {
// TODO: remove this in a separate PR and use the isGroupMember from the social.go // TODO: remove this in a separate PR and use the isGroupMember from the social.go
func (s *SocialOkta) IsGroupMember(groups []string) bool { func (s *SocialOkta) IsGroupMember(groups []string) bool {
if len(s.allowedGroups) == 0 { if len(s.info.AllowedGroups) == 0 {
return true return true
} }
for _, allowedGroup := range s.allowedGroups { for _, allowedGroup := range s.info.AllowedGroups {
for _, group := range groups { for _, group := range groups {
if group == allowedGroup { if group == allowedGroup {
return true return true

View File

@ -22,18 +22,10 @@ import (
type SocialBase struct { type SocialBase struct {
*oauth2.Config *oauth2.Config
info *social.OAuthInfo info *social.OAuthInfo
log log.Logger log log.Logger
allowSignup bool autoAssignOrgRole string
allowAssignGrafanaAdmin bool features featuremgmt.FeatureManager
allowedDomains []string
allowedGroups []string
roleAttributePath string
roleAttributeStrict bool
autoAssignOrgRole string
features featuremgmt.FeatureManager
useRefreshToken bool
} }
func newSocialBase(name string, func newSocialBase(name string,
@ -45,18 +37,11 @@ func newSocialBase(name string,
logger := log.New("oauth." + name) logger := log.New("oauth." + name)
return &SocialBase{ return &SocialBase{
Config: config, Config: config,
info: info, info: info,
log: logger, log: logger,
allowSignup: info.AllowSignup, autoAssignOrgRole: autoAssignOrgRole,
allowAssignGrafanaAdmin: info.AllowAssignGrafanaAdmin, features: features,
allowedDomains: info.AllowedDomains,
allowedGroups: info.AllowedGroups,
roleAttributePath: info.RoleAttributePath,
roleAttributeStrict: info.RoleAttributeStrict,
autoAssignOrgRole: autoAssignOrgRole,
features: features,
useRefreshToken: info.UseRefreshToken,
} }
} }
@ -67,12 +52,12 @@ type groupStruct struct {
func (s *SocialBase) SupportBundleContent(bf *bytes.Buffer) error { func (s *SocialBase) SupportBundleContent(bf *bytes.Buffer) error {
bf.WriteString("## Client configuration\n\n") bf.WriteString("## Client configuration\n\n")
bf.WriteString("```ini\n") bf.WriteString("```ini\n")
bf.WriteString(fmt.Sprintf("allow_assign_grafana_admin = %v\n", s.allowAssignGrafanaAdmin)) bf.WriteString(fmt.Sprintf("allow_assign_grafana_admin = %v\n", s.info.AllowAssignGrafanaAdmin))
bf.WriteString(fmt.Sprintf("allow_sign_up = %v\n", s.allowSignup)) bf.WriteString(fmt.Sprintf("allow_sign_up = %v\n", s.info.AllowSignup))
bf.WriteString(fmt.Sprintf("allowed_domains = %v\n", s.allowedDomains)) bf.WriteString(fmt.Sprintf("allowed_domains = %v\n", s.info.AllowedDomains))
bf.WriteString(fmt.Sprintf("auto_assign_org_role = %v\n", s.autoAssignOrgRole)) bf.WriteString(fmt.Sprintf("auto_assign_org_role = %v\n", s.autoAssignOrgRole))
bf.WriteString(fmt.Sprintf("role_attribute_path = %v\n", s.roleAttributePath)) bf.WriteString(fmt.Sprintf("role_attribute_path = %v\n", s.info.RoleAttributePath))
bf.WriteString(fmt.Sprintf("role_attribute_strict = %v\n", s.roleAttributeStrict)) bf.WriteString(fmt.Sprintf("role_attribute_strict = %v\n", s.info.RoleAttributeStrict))
bf.WriteString(fmt.Sprintf("skip_org_role_sync = %v\n", s.info.SkipOrgRoleSync)) bf.WriteString(fmt.Sprintf("skip_org_role_sync = %v\n", s.info.SkipOrgRoleSync))
bf.WriteString(fmt.Sprintf("client_id = %v\n", s.Config.ClientID)) bf.WriteString(fmt.Sprintf("client_id = %v\n", s.Config.ClientID))
bf.WriteString(fmt.Sprintf("client_secret = %v ; issue if empty\n", strings.Repeat("*", len(s.Config.ClientSecret)))) bf.WriteString(fmt.Sprintf("client_secret = %v ; issue if empty\n", strings.Repeat("*", len(s.Config.ClientSecret))))
@ -86,8 +71,8 @@ func (s *SocialBase) SupportBundleContent(bf *bytes.Buffer) error {
} }
func (s *SocialBase) extractRoleAndAdminOptional(rawJSON []byte, groups []string) (org.RoleType, bool, error) { func (s *SocialBase) extractRoleAndAdminOptional(rawJSON []byte, groups []string) (org.RoleType, bool, error) {
if s.roleAttributePath == "" { if s.info.RoleAttributePath == "" {
if s.roleAttributeStrict { if s.info.RoleAttributeStrict {
return "", false, errRoleAttributePathNotSet.Errorf("role_attribute_path not set and role_attribute_strict is set") return "", false, errRoleAttributePathNotSet.Errorf("role_attribute_path not set and role_attribute_strict is set")
} }
return "", false, nil return "", false, nil
@ -99,7 +84,7 @@ func (s *SocialBase) extractRoleAndAdminOptional(rawJSON []byte, groups []string
return "", false, errInvalidRole.Errorf("invalid role: %s", role) return "", false, errInvalidRole.Errorf("invalid role: %s", role)
} }
if s.roleAttributeStrict { if s.info.RoleAttributeStrict {
return "", false, errRoleAttributeStrictViolation.Errorf("idP did not return a role attribute, but role_attribute_strict is set") return "", false, errRoleAttributeStrictViolation.Errorf("idP did not return a role attribute, but role_attribute_strict is set")
} }
@ -116,13 +101,13 @@ func (s *SocialBase) extractRoleAndAdmin(rawJSON []byte, groups []string) (org.R
} }
func (s *SocialBase) searchRole(rawJSON []byte, groups []string) (org.RoleType, bool) { func (s *SocialBase) searchRole(rawJSON []byte, groups []string) (org.RoleType, bool) {
role, err := s.searchJSONForStringAttr(s.roleAttributePath, rawJSON) role, err := s.searchJSONForStringAttr(s.info.RoleAttributePath, rawJSON)
if err == nil && role != "" { if err == nil && role != "" {
return getRoleFromSearch(role) return getRoleFromSearch(role)
} }
if groupBytes, err := json.Marshal(groupStruct{groups}); err == nil { if groupBytes, err := json.Marshal(groupStruct{groups}); err == nil {
role, err := s.searchJSONForStringAttr(s.roleAttributePath, groupBytes) role, err := s.searchJSONForStringAttr(s.info.RoleAttributePath, groupBytes)
if err == nil && role != "" { if err == nil && role != "" {
return getRoleFromSearch(role) return getRoleFromSearch(role)
} }
@ -144,11 +129,11 @@ func (s *SocialBase) defaultRole() org.RoleType {
} }
func (s *SocialBase) isGroupMember(groups []string) bool { func (s *SocialBase) isGroupMember(groups []string) bool {
if len(s.allowedGroups) == 0 { if len(s.info.AllowedGroups) == 0 {
return true return true
} }
for _, allowedGroup := range s.allowedGroups { for _, allowedGroup := range s.info.AllowedGroups {
for _, group := range groups { for _, group := range groups {
if group == allowedGroup { if group == allowedGroup {
return true return true