Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to fetch indirect group memberships from Google #2077

Closed
wants to merge 1 commit into from
Closed

Add option to fetch indirect group memberships from Google #2077

wants to merge 1 commit into from

Conversation

santisaez
Copy link

Overview

Google Workspace groups (aka G Suite) have support for nesting groups, that is, add a group to another group. This is useful to automatically add members from a small team (backend, for example) to a larger department (engineering, for example).

A user in Google Workspace can have both direct and indirect group memberships. In the example above, "backend" is a direct group and "engineering" is an indirect group membership. The Google Admin SDK API only returns direct group memberships, and thus dex only returns direct memberships.

What this PR does / why we need it

This PR extends the work done by @JoelSpeed in #1185 and adds an option to fetch the indirect group memberships from Google Workspace, aka G Suite. This feature will be useful to fetch all groups a user belongs to, both direct and indirect.

Special notes for your reviewer

cc/ @JoelSpeed because they developed the feature to fetch the groups from Google

Does this PR introduce a user-facing change?

google: groupsIndirectFetch option added to fetch indirect group memberships from Google

@@ -254,14 +259,23 @@ func (c *googleConnector) getGroups(email string) ([]string, error) {
for _, group := range groupsList.Groups {
// TODO (joelspeed): Make desired group key configurable
userGroups = append(userGroups, group.Email)

if groupsIndirectFetch {
indirectGroups, err := c.getGroups(group.Email, groupsIndirectFetch)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quoting from https://developers.google.com/admin-sdk/directory/v1/guides/manage-groups#get_all_member_groups:

The userKey can be the user's primary email address, the user's alias email address, a group's primary email address, a group's email alias, or the user's unique id

And when a group is provided as userKey the parent groups will be returned.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to add some of this context into the function as a comment IMO

Copy link
Contributor

@JoelSpeed JoelSpeed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, just a question about the naming and a couple of nits, otherwise LGTM

How have you tested this so far?

Comment on lines +53 to +54
// If this field is true, both direct and indirect group memberships will be fetched
GroupsIndirectFetch bool `json:"groupsIndirectFetch"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to have a more explicit naming here.

Eg. FetchIndirectGroupMemberships or FetchTransitiveGroupMembership, WDYT?

@@ -254,14 +259,23 @@ func (c *googleConnector) getGroups(email string) ([]string, error) {
for _, group := range groupsList.Groups {
// TODO (joelspeed): Make desired group key configurable
userGroups = append(userGroups, group.Email)

if groupsIndirectFetch {
indirectGroups, err := c.getGroups(group.Email, groupsIndirectFetch)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to add some of this context into the function as a comment IMO

Comment on lines +316 to +327
keys := make(map[string]bool)
unique := []string{}

for _, group := range groups {
if _, value := keys[group]; !value {
keys[group] = true
unique = append(unique, group)
}
}

return unique
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you use a struct, it's technically more efficient, I would do this as (this is very much a nit)

Suggested change
keys := make(map[string]bool)
unique := []string{}
for _, group := range groups {
if _, value := keys[group]; !value {
keys[group] = true
unique = append(unique, group)
}
}
return unique
}
keys := make(map[string]struct{})
unique := []string{}
for _, group := range groups {
if _, exists := keys[group]; !exists {
keys[group] = struct{}{}
unique = append(unique, group)
}
}
return unique
}

@jasonhadi
Copy link

Hey @santisaez and Dex folks, anything I can do to help to help move this PR forward? Would love to be able to use nested groups with Dex.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants