@@ -49,6 +49,9 @@ type Config struct {
49
49
// The email of a GSuite super user which the service account will impersonate
50
50
// when listing groups
51
51
AdminEmail string
52
+
53
+ // If this field is true, fetch direct group membership and transitive group membership
54
+ FetchTransitiveGroupMembership bool `json:"fetchTransitiveGroupMembership"`
52
55
}
53
56
54
57
// Open returns a connector which can be used to login users through Google.
@@ -87,13 +90,14 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
87
90
verifier : provider .Verifier (
88
91
& oidc.Config {ClientID : clientID },
89
92
),
90
- logger : logger ,
91
- cancel : cancel ,
92
- hostedDomains : c .HostedDomains ,
93
- groups : c .Groups ,
94
- serviceAccountFilePath : c .ServiceAccountFilePath ,
95
- adminEmail : c .AdminEmail ,
96
- adminSrv : srv ,
93
+ logger : logger ,
94
+ cancel : cancel ,
95
+ hostedDomains : c .HostedDomains ,
96
+ groups : c .Groups ,
97
+ serviceAccountFilePath : c .ServiceAccountFilePath ,
98
+ adminEmail : c .AdminEmail ,
99
+ fetchTransitiveGroupMembership : c .FetchTransitiveGroupMembership ,
100
+ adminSrv : srv ,
97
101
}, nil
98
102
}
99
103
@@ -103,16 +107,17 @@ var (
103
107
)
104
108
105
109
type googleConnector struct {
106
- redirectURI string
107
- oauth2Config * oauth2.Config
108
- verifier * oidc.IDTokenVerifier
109
- cancel context.CancelFunc
110
- logger log.Logger
111
- hostedDomains []string
112
- groups []string
113
- serviceAccountFilePath string
114
- adminEmail string
115
- adminSrv * admin.Service
110
+ redirectURI string
111
+ oauth2Config * oauth2.Config
112
+ verifier * oidc.IDTokenVerifier
113
+ cancel context.CancelFunc
114
+ logger log.Logger
115
+ hostedDomains []string
116
+ groups []string
117
+ serviceAccountFilePath string
118
+ adminEmail string
119
+ fetchTransitiveGroupMembership bool
120
+ adminSrv * admin.Service
116
121
}
117
122
118
123
func (c * googleConnector ) Close () error {
@@ -214,7 +219,7 @@ func (c *googleConnector) createIdentity(ctx context.Context, identity connector
214
219
215
220
var groups []string
216
221
if s .Groups && c .adminSrv != nil {
217
- groups , err = c .getGroups (claims .Email )
222
+ groups , err = c .getGroups (claims .Email , c . fetchTransitiveGroupMembership )
218
223
if err != nil {
219
224
return identity , fmt .Errorf ("google: could not retrieve groups: %v" , err )
220
225
}
@@ -240,7 +245,7 @@ func (c *googleConnector) createIdentity(ctx context.Context, identity connector
240
245
241
246
// getGroups creates a connection to the admin directory service and lists
242
247
// all groups the user is a member of
243
- func (c * googleConnector ) getGroups (email string ) ([]string , error ) {
248
+ func (c * googleConnector ) getGroups (email string , fetchTransitiveGroupMembership bool ) ([]string , error ) {
244
249
var userGroups []string
245
250
var err error
246
251
groupsList := & admin.Groups {}
@@ -254,14 +259,24 @@ func (c *googleConnector) getGroups(email string) ([]string, error) {
254
259
for _ , group := range groupsList .Groups {
255
260
// TODO (joelspeed): Make desired group key configurable
256
261
userGroups = append (userGroups , group .Email )
262
+
263
+ // getGroups takes a user's email/alias as well as a group's email/alias
264
+ if fetchTransitiveGroupMembership {
265
+ transitiveGroups , err := c .getGroups (group .Email , fetchTransitiveGroupMembership )
266
+ if err != nil {
267
+ return nil , fmt .Errorf ("could not list transitive groups: %v" , err )
268
+ }
269
+
270
+ userGroups = append (userGroups , transitiveGroups ... )
271
+ }
257
272
}
258
273
259
274
if groupsList .NextPageToken == "" {
260
275
break
261
276
}
262
277
}
263
278
264
- return userGroups , nil
279
+ return uniqueGroups ( userGroups ) , nil
265
280
}
266
281
267
282
// createDirectoryService loads a google service account credentials file,
@@ -296,3 +311,16 @@ func createDirectoryService(serviceAccountFilePath string, email string) (*admin
296
311
}
297
312
return srv , nil
298
313
}
314
+
315
+ // uniqueGroups returns the unique groups of a slice
316
+ func uniqueGroups (groups []string ) []string {
317
+ keys := make (map [string ]struct {})
318
+ unique := []string {}
319
+ for _ , group := range groups {
320
+ if _ , exists := keys [group ]; ! exists {
321
+ keys [group ] = struct {}{}
322
+ unique = append (unique , group )
323
+ }
324
+ }
325
+ return unique
326
+ }
0 commit comments