Skip to content

Commit 4585850

Browse files
committed
microsoft: option for group UUIDs instead of name and group whitelist
1 parent 20a858d commit 4585850

File tree

2 files changed

+65
-29
lines changed

2 files changed

+65
-29
lines changed

Documentation/connectors/microsoft.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ a member of. `onlySecurityGroups` configuration option restricts the list to
8888
include only security groups. By default all groups (security, Office 365,
8989
mailing lists) are included.
9090

91+
By default, dex resolve groups ids to groups names, to keep groups ids, you can
92+
specify the configuration option `groupNameFormat: id`.
93+
9194
It is possible to require a user to be a member of a particular group in order
9295
to be successfully authenticated in dex. For example, with the following
9396
configuration file only the users who are members of at least one of the listed
@@ -110,3 +113,6 @@ connectors:
110113
- developers
111114
- devops
112115
```
116+
117+
Also, `useGroupsAsWhitelist` configuration option, can restrict the groups
118+
claims to include only the user's groups that are in the configured `groups`.

connector/microsoft/microsoft.go

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,42 +19,66 @@ import (
1919
"github.com/dexidp/dex/pkg/log"
2020
)
2121

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+
2233
const (
2334
apiURL = "https://graph.microsoft.com"
2435
// Microsoft requires this scope to access user's profile
2536
scopeUser = "user.read"
2637
// 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.
2839
scopeGroups = "directory.read.all"
2940
)
3041

3142
// Config holds configuration options for microsoft logins.
3243
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"`
3952
}
4053

4154
// Open returns a strategy for logging in through Microsoft.
4255
func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
4356
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,
5166
}
5267
// By default allow logins from both personal and business/school
5368
// accounts.
5469
if m.tenant == "" {
5570
m.tenant = "common"
5671
}
5772

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+
5882
return &m, nil
5983
}
6084

@@ -70,13 +94,15 @@ var (
7094
)
7195

7296
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
80106
}
81107

82108
func (c *microsoftConnector) isOrgTenant() bool {
@@ -300,24 +326,28 @@ type group struct {
300326
Name string `json:"displayName"`
301327
}
302328

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)
305331
if err != nil {
306-
return groups, err
332+
return nil, err
307333
}
308334

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+
}
312340
}
313341

314342
// 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)
316344
if len(c.groups) > 0 && len(filteredGroups) == 0 {
317345
return nil, fmt.Errorf("microsoft: user %v not in any of the required groups", userID)
346+
} else if c.useGroupsAsWhitelist {
347+
return filteredGroups, nil
318348
}
319349

320-
return
350+
return userGroups, nil
321351
}
322352

323353
func (c *microsoftConnector) getGroupIDs(ctx context.Context, client *http.Client) (ids []string, err error) {

0 commit comments

Comments
 (0)