Skip to content

Commit e99dff0

Browse files
committed
Added primary_user config map and wiring
1 parent abfa912 commit e99dff0

3 files changed

Lines changed: 119 additions & 13 deletions

File tree

cache/user.go

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ import (
2020
// user:groups:<opaqueID> → JSON []string
2121

2222
const (
23-
userIDPrefix = "user:id:"
24-
userUsernamePrefix = "user:username:"
25-
userMailPrefix = "user:mail:"
26-
userNamePrefix = "user:name:"
27-
userGroupsPrefix = "user:groups:"
23+
userIDPrefix = "user:id:"
24+
userUsernamePrefix = "user:username:"
25+
userMailPrefix = "user:mail:"
26+
userNamePrefix = "user:name:"
27+
userGroupsPrefix = "user:groups:"
28+
userIAMUUIDPrefix = "user:iamuuid:" // opaqueID -> IAM account UUID
29+
userOpaqueIDByIAMUUID = "user:byiamuuid:" // IAM account UUID -> opaqueID
2830
)
2931

3032
// UserCache is a Redis-backed cache for CS3 user objects and their group
@@ -144,3 +146,42 @@ func (c *UserCache) GetGroups(ctx context.Context, opaqueID string) ([]string, e
144146
}
145147
return groups, nil
146148
}
149+
150+
// StoreIAMUUID records the two-way mapping between a remapped OpaqueId and
151+
// the original IAM account UUID, so callers can resolve in either direction:
152+
// - GetIAMUUID: opaqueID -> iamUUID (used by the user driver for
153+
// group-membership lookups against the IAM API)
154+
// - GetOpaqueIDByIAMUUID: iamUUID -> opaqueID (used by the group driver
155+
// to report the same OpaqueId for a member that GetUser/FindUsers
156+
// would report)
157+
//
158+
// Only called for accounts that were actually remapped via primary_users;
159+
// the common case (OpaqueId == IAM UUID) needs no entry in either direction.
160+
func (c *UserCache) StoreIAMUUID(opaqueID, iamUUID string) error {
161+
if err := store(c.pools, userIAMUUIDPrefix+strings.ToLower(opaqueID), iamUUID, c.userTTLSecs); err != nil {
162+
return err
163+
}
164+
return store(c.pools, userOpaqueIDByIAMUUID+strings.ToLower(iamUUID), opaqueID, c.userTTLSecs)
165+
}
166+
167+
// GetIAMUUID resolves the IAM account UUID for a given OpaqueId. If no
168+
// mapping is present, callers should fall back to treating opaqueID itself
169+
// as the IAM UUID.
170+
func (c *UserCache) GetIAMUUID(ctx context.Context, opaqueID string) (string, error) {
171+
var uuid string
172+
if err := fetch(ctx, c.pools, userIAMUUIDPrefix+strings.ToLower(opaqueID), &uuid); err != nil {
173+
return "", err
174+
}
175+
return uuid, nil
176+
}
177+
178+
// GetOpaqueIDByIAMUUID resolves the public OpaqueId for a given IAM account
179+
// UUID. If no mapping is present, callers should fall back to treating the
180+
// IAM UUID itself as the OpaqueId (the common, non-remapped case).
181+
func (c *UserCache) GetOpaqueIDByIAMUUID(ctx context.Context, iamUUID string) (string, error) {
182+
var opaqueID string
183+
if err := fetch(ctx, c.pools, userOpaqueIDByIAMUUID+strings.ToLower(iamUUID), &opaqueID); err != nil {
184+
return "", err
185+
}
186+
return opaqueID, nil
187+
}

group/indigoiam/rest.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ func init() {
5050
type manager struct {
5151
conf *config
5252
cache *cache.GroupCache
53+
userCache *cache.UserCache // read-only access, for IAM-UUID -> OpaqueId resolution
5354
httpClient *http.Client
5455
}
5556

@@ -177,6 +178,13 @@ func New(ctx context.Context, m map[string]interface{}) (group.Manager, error) {
177178
mgr := &manager{
178179
conf: &c,
179180
cache: cache.NewGroupCache(pools, c.GroupFetchInterval, c.GroupMembersCacheExpiration),
181+
// UserGroupsCacheExpiration doesn't apply here since this UserCache
182+
// instance is only ever used for GetIAMUUID/GetOpaqueIDByIAMUUID
183+
// lookups, not for storing users or groups membership. The TTL
184+
// arguments only affect StoreUser/StoreGroups, which this driver
185+
// never calls, so any values are safe; we reuse the group driver's
186+
// own fetch interval for consistency.
187+
userCache: cache.NewUserCache(pools, c.GroupFetchInterval, c.GroupMembersCacheExpiration),
180188
httpClient: &http.Client{
181189
Transport: tr,
182190
Timeout: time.Duration(c.HTTPTimeoutSeconds) * time.Second,
@@ -332,10 +340,19 @@ func (m *manager) GetMembers(ctx context.Context, gid *grouppb.GroupId) ([]*user
332340
if !acc.Active {
333341
continue
334342
}
343+
344+
opaqueID := acc.ID
345+
userType := userpb.UserType_USER_TYPE_LIGHTWEIGHT
346+
347+
if mapped, err := m.userCache.GetOpaqueIDByIAMUUID(ctx, acc.ID); err == nil {
348+
opaqueID = mapped
349+
userType = userpb.UserType_USER_TYPE_PRIMARY
350+
}
351+
335352
members = append(members, &userpb.UserId{
336-
OpaqueId: acc.ID,
353+
OpaqueId: opaqueID,
337354
Idp: m.conf.IDProvider,
338-
Type: userpb.UserType_USER_TYPE_PRIMARY,
355+
Type: userType,
339356
})
340357
}
341358

user/indigoiam/rest.go

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ type manager struct {
5151
httpClient *http.Client
5252
}
5353

54+
type primaryUser struct {
55+
username string
56+
uid_number int64
57+
gid_number int64
58+
}
59+
5460
func (manager) RevaPlugin() reva.PluginInfo {
5561
return reva.PluginInfo{
5662
ID: "grpc.services.userprovider.drivers.indigo-iam",
@@ -75,6 +81,14 @@ type config struct {
7581
UserFetchInterval int `mapstructure:"user_fetch_interval" docs:"3600"`
7682
UserGroupsCacheExpiration int `mapstructure:"user_groups_cache_expiration" docs:"5"`
7783
PageSize int `mapstructure:"page_size" docs:"100"`
84+
85+
// PrimaryUsers maps an IAM account UUID to an internal user with the given
86+
// username, uid and gid, and marks that user as USER_TYPE_PRIMARY. Every other
87+
// user is treated as USER_TYPE_LIGHTWEIGHT. This is needed because IAM
88+
// has no native concept of primary vs. lightweight accounts: by default
89+
// every account looks the same, so an explicit allowlist is required to
90+
// recognize the subset of "real" organization members.
91+
PrimaryUsers map[string]primaryUser `mapstructure:"primary_users" docs:"{}"`
7892
}
7993

8094
func (c *config) ApplyDefaults() {
@@ -261,10 +275,17 @@ func (m *manager) fetchAllUserAccounts(ctx context.Context) error {
261275
if !acc.Active {
262276
continue
263277
}
264-
u := m.accountToProto(acc)
278+
u, remapped := m.accountToProto(acc)
265279
if err := m.cache.StoreUser(u); err != nil {
266280
log.Error().Err(err).Str("uuid", acc.ID).Msg("indigoiam user: cache error")
267281
}
282+
// Only the exceptional, remapped case needs a reverse-index
283+
// entry; lightweight users already have OpaqueId == IAM UUID.
284+
if remapped {
285+
if err := m.cache.StoreIAMUUID(u.Id.OpaqueId, acc.ID); err != nil {
286+
log.Error().Err(err).Str("uuid", acc.ID).Msg("indigoiam user: failed to cache IAM UUID mapping")
287+
}
288+
}
268289
}
269290

270291
nextStart := startIndex + list.ItemsPerPage
@@ -276,17 +297,36 @@ func (m *manager) fetchAllUserAccounts(ctx context.Context) error {
276297
return nil
277298
}
278299

279-
func (m *manager) accountToProto(acc *iamAccount) *userpb.User {
300+
// accountToProto converts an iamAccount to the CS3 userpb.User type.
301+
// If acc.ID is a key in conf.PrimaryUsers, the OpaqueId is replaced by the
302+
// mapped value and the user is marked PRIMARY; otherwise the user is
303+
// LIGHTWEIGHT and keeps its original IAM UUID as OpaqueId.
304+
func (m *manager) accountToProto(acc *iamAccount) (*userpb.User, bool) {
305+
opaqueID := acc.ID
306+
userType := userpb.UserType_USER_TYPE_LIGHTWEIGHT
307+
uid := int64(0)
308+
gid := int64(0)
309+
remapped := false
310+
311+
if mapped, remapped := m.conf.PrimaryUsers[acc.ID]; remapped {
312+
opaqueID = mapped.username
313+
uid = int64(mapped.uid_number)
314+
gid = int64(mapped.gid_number)
315+
userType = userpb.UserType_USER_TYPE_PRIMARY
316+
}
317+
280318
return &userpb.User{
281319
Id: &userpb.UserId{
282-
OpaqueId: acc.ID,
320+
OpaqueId: opaqueID,
283321
Idp: m.conf.IDProvider,
284-
Type: userpb.UserType_USER_TYPE_PRIMARY,
322+
Type: userType,
285323
},
286324
Username: acc.UserName,
287325
Mail: acc.primaryEmail(),
288326
DisplayName: acc.DisplayName,
289-
}
327+
UidNumber: uid,
328+
GidNumber: gid,
329+
}, remapped
290330
}
291331

292332
// ---------------------------------------------------------------------------
@@ -364,7 +404,15 @@ func (m *manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]stri
364404
return cached, nil
365405
}
366406

367-
url := fmt.Sprintf("%s/iam/account/%s/groups", m.conf.IAMBaseURL, uid.OpaqueId)
407+
iamUUID, err := m.cache.GetIAMUUID(ctx, uid.OpaqueId)
408+
if err != nil {
409+
// No mapping found — assume the OpaqueId is already the IAM UUID
410+
// (this is always true for lightweight users, since they are never
411+
// remapped).
412+
iamUUID = uid.OpaqueId
413+
}
414+
415+
url := fmt.Sprintf("%s/iam/account/%s/groups", m.conf.IAMBaseURL, iamUUID)
368416
var list iamGroupList
369417
if err := m.iamGET(ctx, url, &list); err != nil {
370418
return nil, err

0 commit comments

Comments
 (0)