@@ -19,42 +19,66 @@ import (
19
19
"github.com/dexidp/dex/pkg/log"
20
20
)
21
21
22
+ // GroupNameFormat represents the format of the group identifier
23
+ // we use type of string instead of int because it's easier to
24
+ // marshall/unmarshall
25
+ type GroupNameFormat string
26
+
27
+ // Possible values for GroupNameFormat
28
+ const (
29
+ GroupID GroupNameFormat = "id"
30
+ GroupName GroupNameFormat = "name"
31
+ )
32
+
22
33
const (
23
34
apiURL = "https://graph.microsoft.com"
24
35
// Microsoft requires this scope to access user's profile
25
36
scopeUser = "user.read"
26
37
// Microsoft requires this scope to list groups the user is a member of
27
- // and resolve their UUIDs to groups names.
38
+ // and resolve their ids to groups names.
28
39
scopeGroups = "directory.read.all"
29
40
)
30
41
31
42
// Config holds configuration options for microsoft logins.
32
43
type Config struct {
33
- ClientID string `json:"clientID"`
34
- ClientSecret string `json:"clientSecret"`
35
- RedirectURI string `json:"redirectURI"`
36
- Tenant string `json:"tenant"`
37
- OnlySecurityGroups bool `json:"onlySecurityGroups"`
38
- Groups []string `json:"groups"`
44
+ ClientID string `json:"clientID"`
45
+ ClientSecret string `json:"clientSecret"`
46
+ RedirectURI string `json:"redirectURI"`
47
+ Tenant string `json:"tenant"`
48
+ OnlySecurityGroups bool `json:"onlySecurityGroups"`
49
+ Groups []string `json:"groups"`
50
+ GroupNameFormat GroupNameFormat `json:"groupNameFormat"`
51
+ UseGroupsAsWhitelist bool `json:"useGroupsAsWhitelist"`
39
52
}
40
53
41
54
// Open returns a strategy for logging in through Microsoft.
42
55
func (c * Config ) Open (id string , logger log.Logger ) (connector.Connector , error ) {
43
56
m := microsoftConnector {
44
- redirectURI : c .RedirectURI ,
45
- clientID : c .ClientID ,
46
- clientSecret : c .ClientSecret ,
47
- tenant : c .Tenant ,
48
- onlySecurityGroups : c .OnlySecurityGroups ,
49
- groups : c .Groups ,
50
- logger : logger ,
57
+ redirectURI : c .RedirectURI ,
58
+ clientID : c .ClientID ,
59
+ clientSecret : c .ClientSecret ,
60
+ tenant : c .Tenant ,
61
+ onlySecurityGroups : c .OnlySecurityGroups ,
62
+ groups : c .Groups ,
63
+ groupNameFormat : c .GroupNameFormat ,
64
+ useGroupsAsWhitelist : c .UseGroupsAsWhitelist ,
65
+ logger : logger ,
51
66
}
52
67
// By default allow logins from both personal and business/school
53
68
// accounts.
54
69
if m .tenant == "" {
55
70
m .tenant = "common"
56
71
}
57
72
73
+ // By default, use group names
74
+ switch m .groupNameFormat {
75
+ case "" :
76
+ m .groupNameFormat = GroupName
77
+ case GroupID , GroupName :
78
+ default :
79
+ return nil , fmt .Errorf ("invalid groupNameFormat: %s" , m .groupNameFormat )
80
+ }
81
+
58
82
return & m , nil
59
83
}
60
84
@@ -70,13 +94,15 @@ var (
70
94
)
71
95
72
96
type microsoftConnector struct {
73
- redirectURI string
74
- clientID string
75
- clientSecret string
76
- tenant string
77
- onlySecurityGroups bool
78
- groups []string
79
- logger log.Logger
97
+ redirectURI string
98
+ clientID string
99
+ clientSecret string
100
+ tenant string
101
+ onlySecurityGroups bool
102
+ groupNameFormat GroupNameFormat
103
+ groups []string
104
+ useGroupsAsWhitelist bool
105
+ logger log.Logger
80
106
}
81
107
82
108
func (c * microsoftConnector ) isOrgTenant () bool {
@@ -300,24 +326,28 @@ type group struct {
300
326
Name string `json:"displayName"`
301
327
}
302
328
303
- func (c * microsoftConnector ) getGroups (ctx context.Context , client * http.Client , userID string ) (groups []string , err error ) {
304
- ids , err := c .getGroupIDs (ctx , client )
329
+ func (c * microsoftConnector ) getGroups (ctx context.Context , client * http.Client , userID string ) ([]string , error ) {
330
+ userGroups , err := c .getGroupIDs (ctx , client )
305
331
if err != nil {
306
- return groups , err
332
+ return nil , err
307
333
}
308
334
309
- groups , err = c .getGroupNames (ctx , client , ids )
310
- if err != nil {
311
- return
335
+ if c .groupNameFormat == GroupName {
336
+ userGroups , err = c .getGroupNames (ctx , client , userGroups )
337
+ if err != nil {
338
+ return nil , err
339
+ }
312
340
}
313
341
314
342
// ensure that the user is in at least one required group
315
- filteredGroups := groups_pkg .Filter (groups , c .groups )
343
+ filteredGroups := groups_pkg .Filter (userGroups , c .groups )
316
344
if len (c .groups ) > 0 && len (filteredGroups ) == 0 {
317
345
return nil , fmt .Errorf ("microsoft: user %v not in any of the required groups" , userID )
346
+ } else if c .useGroupsAsWhitelist {
347
+ return filteredGroups , nil
318
348
}
319
349
320
- return
350
+ return userGroups , nil
321
351
}
322
352
323
353
func (c * microsoftConnector ) getGroupIDs (ctx context.Context , client * http.Client ) (ids []string , err error ) {
0 commit comments