Azure OAuth: enable teamsync (#22160)
* Azure OAuth: extract groups from token for teamsync * Docs: changed some headers * Azure OAuth: fix tests * Azure OAuth: fix linter error (simplify) * Azure OAuth: add allowed_groups option * Azure OAuth: docs for team sync and allowed_groups * Azure OAuth: tests for allowed_groups * Update docs/sources/auth/azuread.md Co-Authored-By: Leonard Gram <leo@xlson.com> Co-authored-by: Leonard Gram <leo@xlson.com>
This commit is contained in:
co-authored by
Leonard Gram
parent
961cb6b284
commit
f2fc7aa3aa
@@ -15,6 +15,7 @@ import (
|
||||
type SocialAzureAD struct {
|
||||
*SocialBase
|
||||
allowedDomains []string
|
||||
allowedGroups []string
|
||||
allowSignup bool
|
||||
}
|
||||
|
||||
@@ -22,6 +23,7 @@ type azureClaims struct {
|
||||
Email string `json:"email"`
|
||||
PreferredUsername string `json:"preferred_username"`
|
||||
Roles []string `json:"roles"`
|
||||
Groups []string `json:"groups"`
|
||||
Name string `json:"name"`
|
||||
ID string `json:"oid"`
|
||||
}
|
||||
@@ -62,15 +64,37 @@ func (s *SocialAzureAD) UserInfo(_ *http.Client, token *oauth2.Token) (*BasicUse
|
||||
|
||||
role := extractRole(claims)
|
||||
|
||||
groups := extractGroups(claims)
|
||||
if !s.IsGroupMember(groups) {
|
||||
return nil, ErrMissingGroupMembership
|
||||
}
|
||||
|
||||
return &BasicUserInfo{
|
||||
Id: claims.ID,
|
||||
Name: claims.Name,
|
||||
Email: email,
|
||||
Login: email,
|
||||
Role: string(role),
|
||||
Id: claims.ID,
|
||||
Name: claims.Name,
|
||||
Email: email,
|
||||
Login: email,
|
||||
Role: string(role),
|
||||
Groups: groups,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *SocialAzureAD) IsGroupMember(groups []string) bool {
|
||||
if len(s.allowedGroups) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, allowedGroup := range s.allowedGroups {
|
||||
for _, group := range groups {
|
||||
if group == allowedGroup {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func extractEmail(claims azureClaims) string {
|
||||
if claims.Email == "" {
|
||||
if claims.PreferredUsername != "" {
|
||||
@@ -109,3 +133,9 @@ func hasRole(roles []string, role models.RoleType) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func extractGroups(claims azureClaims) []string {
|
||||
groups := make([]string, 0)
|
||||
groups = append(groups, claims.Groups...)
|
||||
return groups
|
||||
}
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
package social
|
||||
|
||||
import (
|
||||
"golang.org/x/oauth2"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
"gopkg.in/square/go-jose.v2/jwt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
"gopkg.in/square/go-jose.v2/jwt"
|
||||
)
|
||||
|
||||
func TestSocialAzureAD_UserInfo(t *testing.T) {
|
||||
type fields struct {
|
||||
SocialBase *SocialBase
|
||||
allowedDomains []string
|
||||
allowedGroups []string
|
||||
allowSignup bool
|
||||
}
|
||||
type args struct {
|
||||
@@ -44,7 +46,7 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
||||
Login: "me@example.com",
|
||||
Company: "",
|
||||
Role: "Viewer",
|
||||
Groups: nil,
|
||||
Groups: []string{},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -81,7 +83,7 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
||||
Login: "me@example.com",
|
||||
Company: "",
|
||||
Role: "Viewer",
|
||||
Groups: nil,
|
||||
Groups: []string{},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -100,7 +102,7 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
||||
Login: "me@example.com",
|
||||
Company: "",
|
||||
Role: "Admin",
|
||||
Groups: nil,
|
||||
Groups: []string{},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -119,7 +121,7 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
||||
Login: "me@example.com",
|
||||
Company: "",
|
||||
Role: "Admin",
|
||||
Groups: nil,
|
||||
Groups: []string{},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -138,7 +140,7 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
||||
Login: "me@example.com",
|
||||
Company: "",
|
||||
Role: "Viewer",
|
||||
Groups: nil,
|
||||
Groups: []string{},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -158,7 +160,7 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
||||
Login: "me@example.com",
|
||||
Company: "",
|
||||
Role: "Editor",
|
||||
Groups: nil,
|
||||
Groups: []string{},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -177,7 +179,46 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
||||
Login: "me@example.com",
|
||||
Company: "",
|
||||
Role: "Admin",
|
||||
Groups: nil,
|
||||
Groups: []string{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Error if user is not a member of allowed_groups",
|
||||
fields: fields{
|
||||
allowedGroups: []string{"dead-beef"},
|
||||
},
|
||||
claims: &azureClaims{
|
||||
Email: "me@example.com",
|
||||
PreferredUsername: "",
|
||||
Roles: []string{},
|
||||
Groups: []string{"foo", "bar"},
|
||||
Name: "My Name",
|
||||
ID: "1234",
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Error if user is a member of allowed_groups",
|
||||
fields: fields{
|
||||
allowedGroups: []string{"foo", "bar"},
|
||||
},
|
||||
claims: &azureClaims{
|
||||
Email: "me@example.com",
|
||||
PreferredUsername: "",
|
||||
Roles: []string{},
|
||||
Groups: []string{"foo"},
|
||||
Name: "My Name",
|
||||
ID: "1234",
|
||||
},
|
||||
want: &BasicUserInfo{
|
||||
Id: "1234",
|
||||
Name: "My Name",
|
||||
Email: "me@example.com",
|
||||
Login: "me@example.com",
|
||||
Company: "",
|
||||
Role: "Viewer",
|
||||
Groups: []string{"foo"},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -186,6 +227,7 @@ func TestSocialAzureAD_UserInfo(t *testing.T) {
|
||||
s := &SocialAzureAD{
|
||||
SocialBase: tt.fields.SocialBase,
|
||||
allowedDomains: tt.fields.allowedDomains,
|
||||
allowedGroups: tt.fields.allowedGroups,
|
||||
allowSignup: tt.fields.allowSignup,
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMissingGroupMembership = &Error{"User not a member of one of the required groups"}
|
||||
)
|
||||
|
||||
type HttpGetResponse struct {
|
||||
Body []byte
|
||||
Headers http.Header
|
||||
|
||||
@@ -19,10 +19,6 @@ type SocialGitlab struct {
|
||||
allowSignup bool
|
||||
}
|
||||
|
||||
var (
|
||||
ErrMissingGroupMembership = &Error{"User not a member of one of the required groups"}
|
||||
)
|
||||
|
||||
func (s *SocialGitlab) Type() int {
|
||||
return int(models.GITLAB)
|
||||
}
|
||||
|
||||
@@ -160,6 +160,7 @@ func NewOAuthService() {
|
||||
log: logger,
|
||||
},
|
||||
allowedDomains: info.AllowedDomains,
|
||||
allowedGroups: util.SplitString(sec.Key("allowed_groups").String()),
|
||||
allowSignup: info.AllowSignup,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user