[v10.3.x] OAuth: Improve domain validation (#83177)

OAuth: Improve domain validation (#83110)

* enforce hd claim validation

* add tests

(cherry picked from commit 7e8b679237)

Co-authored-by: linoman <2051016+linoman@users.noreply.github.com>
This commit is contained in:
grafana-delivery-bot[bot]
2024-02-21 16:41:56 +01:00
committed by GitHub
co-authored by linoman
parent a2420cf050
commit 03ae322bee
2 changed files with 64 additions and 0 deletions
@@ -15,6 +15,7 @@ import (
"github.com/grafana/grafana/pkg/services/ssosettings"
ssoModels "github.com/grafana/grafana/pkg/services/ssosettings/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil"
)
const (
@@ -35,6 +36,7 @@ type googleUserData struct {
Email string `json:"email"`
Name string `json:"name"`
EmailVerified bool `json:"email_verified"`
HD string `json:"hd"`
rawJSON []byte `json:"-"`
}
@@ -97,6 +99,10 @@ func (s *SocialGoogle) UserInfo(ctx context.Context, client *http.Client, token
return nil, fmt.Errorf("user email is not verified")
}
if err := s.isHDAllowed(data.HD); err != nil {
return nil, err
}
groups, errPage := s.retrieveGroups(ctx, client, data)
if errPage != nil {
s.log.Warn("Error retrieving groups", "error", errPage)
@@ -272,3 +278,17 @@ func (s *SocialGoogle) getGroupsPage(ctx context.Context, client *http.Client, u
return &data, nil
}
func (s *SocialGoogle) isHDAllowed(hd string) error {
if len(s.info.AllowedDomains) == 0 {
return nil
}
for _, allowedDomain := range s.info.AllowedDomains {
if hd == allowedDomain {
return nil
}
}
return errutil.Forbidden("the hd claim found in the ID token is not present in the allowed domains", errutil.WithPublicMessage("Invalid domain"))
}
@@ -722,3 +722,47 @@ func TestSocialGoogle_Validate(t *testing.T) {
})
}
}
func TestIsHDAllowed(t *testing.T) {
testCases := []struct {
name string
email string
allowedDomains []string
expectedErrorMessage string
}{
{
name: "should not fail if no allowed domains are set",
email: "mycompany.com",
allowedDomains: []string{},
expectedErrorMessage: "",
},
{
name: "should not fail if email is from allowed domain",
email: "mycompany.com",
allowedDomains: []string{"grafana.com", "mycompany.com", "example.com"},
expectedErrorMessage: "",
},
{
name: "should fail if email is not from allowed domain",
email: "mycompany.com",
allowedDomains: []string{"grafana.com", "example.com"},
expectedErrorMessage: "the hd claim found in the ID token is not present in the allowed domains",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
info := &social.OAuthInfo{}
info.AllowedDomains = tc.allowedDomains
s := NewGoogleProvider(info, &setting.Cfg{}, &ssosettingstests.MockService{}, featuremgmt.WithFeatures())
err := s.isHDAllowed(tc.email)
if tc.expectedErrorMessage != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErrorMessage)
} else {
require.NoError(t, err)
}
})
}
}