Skip to content

Commit

Permalink
Resolves dexidp#2111 Option to fetch transitive group membership
Browse files Browse the repository at this point in the history
Signed-off-by: Matt Hoey <[email protected]>
  • Loading branch information
snuggie12 authored and elffjs committed Jun 27, 2022
1 parent 289b513 commit d0cce08
Showing 1 changed file with 48 additions and 20 deletions.
68 changes: 48 additions & 20 deletions connector/google/google.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ type Config struct {
// The email of a GSuite super user which the service account will impersonate
// when listing groups
AdminEmail string

// If this field is true, fetch direct group membership and transitive group membership
FetchTransitiveGroupMembership bool `json:"fetchTransitiveGroupMembership"`
}

// Open returns a connector which can be used to login users through Google.
Expand Down Expand Up @@ -87,13 +90,14 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
verifier: provider.Verifier(
&oidc.Config{ClientID: clientID},
),
logger: logger,
cancel: cancel,
hostedDomains: c.HostedDomains,
groups: c.Groups,
serviceAccountFilePath: c.ServiceAccountFilePath,
adminEmail: c.AdminEmail,
adminSrv: srv,
logger: logger,
cancel: cancel,
hostedDomains: c.HostedDomains,
groups: c.Groups,
serviceAccountFilePath: c.ServiceAccountFilePath,
adminEmail: c.AdminEmail,
fetchTransitiveGroupMembership: c.FetchTransitiveGroupMembership,
adminSrv: srv,
}, nil
}

Expand All @@ -103,16 +107,17 @@ var (
)

type googleConnector struct {
redirectURI string
oauth2Config *oauth2.Config
verifier *oidc.IDTokenVerifier
cancel context.CancelFunc
logger log.Logger
hostedDomains []string
groups []string
serviceAccountFilePath string
adminEmail string
adminSrv *admin.Service
redirectURI string
oauth2Config *oauth2.Config
verifier *oidc.IDTokenVerifier
cancel context.CancelFunc
logger log.Logger
hostedDomains []string
groups []string
serviceAccountFilePath string
adminEmail string
fetchTransitiveGroupMembership bool
adminSrv *admin.Service
}

func (c *googleConnector) Close() error {
Expand Down Expand Up @@ -214,7 +219,7 @@ func (c *googleConnector) createIdentity(ctx context.Context, identity connector

var groups []string
if s.Groups && c.adminSrv != nil {
groups, err = c.getGroups(claims.Email)
groups, err = c.getGroups(claims.Email, c.fetchTransitiveGroupMembership)
if err != nil {
return identity, fmt.Errorf("google: could not retrieve groups: %v", err)
}
Expand All @@ -240,7 +245,7 @@ func (c *googleConnector) createIdentity(ctx context.Context, identity connector

// getGroups creates a connection to the admin directory service and lists
// all groups the user is a member of
func (c *googleConnector) getGroups(email string) ([]string, error) {
func (c *googleConnector) getGroups(email string, fetchTransitiveGroupMembership bool) ([]string, error) {
var userGroups []string
var err error
groupsList := &admin.Groups{}
Expand All @@ -254,14 +259,24 @@ 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)

// getGroups takes a user's email/alias as well as a group's email/alias
if fetchTransitiveGroupMembership {
transitiveGroups, err := c.getGroups(group.Email, fetchTransitiveGroupMembership)
if err != nil {
return nil, fmt.Errorf("could not list transitive groups: %v", err)
}

userGroups = append(userGroups, transitiveGroups...)
}
}

if groupsList.NextPageToken == "" {
break
}
}

return userGroups, nil
return uniqueGroups(userGroups), nil
}

// createDirectoryService loads a google service account credentials file,
Expand Down Expand Up @@ -296,3 +311,16 @@ func createDirectoryService(serviceAccountFilePath string, email string) (*admin
}
return srv, nil
}

// uniqueGroups returns the unique groups of a slice
func uniqueGroups(groups []string) []string {
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
}

0 comments on commit d0cce08

Please sign in to comment.