Merge pull request #2268 from snuggie12/mh-2111-nested-groups

Resolves #2111 Option to fetch transitive group membership
This commit is contained in:
Joel Speed 2021-10-18 09:46:54 +01:00 committed by GitHub
commit 4aa7e6846f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -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.
@ -93,6 +96,7 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
groups: c.Groups,
serviceAccountFilePath: c.ServiceAccountFilePath,
adminEmail: c.AdminEmail,
fetchTransitiveGroupMembership: c.FetchTransitiveGroupMembership,
adminSrv: srv,
}, nil
}
@ -112,6 +116,7 @@ type googleConnector struct {
groups []string
serviceAccountFilePath string
adminEmail string
fetchTransitiveGroupMembership bool
adminSrv *admin.Service
}
@ -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)
}
@ -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{}
@ -254,6 +259,16 @@ 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 == "" {
@ -261,7 +276,7 @@ func (c *googleConnector) getGroups(email string) ([]string, error) {
}
}
return userGroups, nil
return uniqueGroups(userGroups), nil
}
// createDirectoryService loads a google service account credentials file,
@ -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
}