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:
Alexander Zobnin
2020-02-14 14:03:00 +03:00
committed by GitHub
co-authored by Leonard Gram
parent 961cb6b284
commit f2fc7aa3aa
8 changed files with 132 additions and 26 deletions
+35 -5
View File
@@ -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
}
+52 -10
View File
@@ -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,
}
+4
View File
@@ -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
-4
View File
@@ -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)
}
+1
View File
@@ -160,6 +160,7 @@ func NewOAuthService() {
log: logger,
},
allowedDomains: info.AllowedDomains,
allowedGroups: util.SplitString(sec.Key("allowed_groups").String()),
allowSignup: info.AllowSignup,
}
}