Skip to content

Commit 1791cce

Browse files
committed
Added primary_user config map and wiring
1 parent 1b6210a commit 1791cce

3 files changed

Lines changed: 194 additions & 99 deletions

File tree

cache/user.go

Lines changed: 133 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
package cache
22

33
import (
4-
"context"
5-
"encoding/json"
6-
"fmt"
7-
"strings"
8-
9-
redispools "github.com/cernbox/reva-plugins/redispools"
10-
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
11-
"github.com/cs3org/reva/v3/pkg/appctx"
12-
"github.com/cs3org/reva/v3/pkg/errtypes"
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"strings"
8+
9+
redispools "github.com/cernbox/reva-plugins/redispools"
10+
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
11+
"github.com/cs3org/reva/v3/pkg/appctx"
12+
"github.com/cs3org/reva/v3/pkg/errtypes"
1313
)
1414

1515
// Key schema:
@@ -20,125 +20,167 @@ 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
3133
// membership lists.
3234
type UserCache struct {
33-
pools *redispools.RedisPools
34-
userTTLSecs int // 5 × fetch_interval
35-
groupsTTLSecs int // group membership TTL
35+
pools *redispools.RedisPools
36+
userTTLSecs int // 5 × fetch_interval
37+
groupsTTLSecs int // group membership TTL
3638
}
3739

3840
// NewUserCache creates a UserCache.
39-
// fetchInterval – seconds between full IAM/GRAPPA syncs (used to
40-
// derive user record TTL as 5× this value)
41-
// groupsCacheMinutes – TTL for per-user group membership lists
41+
//
42+
// fetchInterval – seconds between full IAM/GRAPPA syncs (used to
43+
// derive user record TTL as 5× this value)
44+
// groupsCacheMinutes – TTL for per-user group membership lists
4245
func NewUserCache(pools *redispools.RedisPools, fetchInterval, groupsCacheMinutes int) *UserCache {
43-
return &UserCache{
44-
pools: pools,
45-
userTTLSecs: 5 * fetchInterval,
46-
groupsTTLSecs: groupsCacheMinutes * 60,
47-
}
46+
return &UserCache{
47+
pools: pools,
48+
userTTLSecs: 5 * fetchInterval,
49+
groupsTTLSecs: groupsCacheMinutes * 60,
50+
}
4851
}
4952

5053
// StoreUser writes a user under all applicable index keys.
5154
func (c *UserCache) StoreUser(u *userpb.User) error {
52-
if err := store(c.pools, userIDPrefix+strings.ToLower(u.Id.OpaqueId), u, c.userTTLSecs); err != nil {
53-
return err
54-
}
55-
if u.Username != "" {
56-
if err := store(c.pools, userUsernamePrefix+strings.ToLower(u.Username), u, c.userTTLSecs); err != nil {
57-
return err
58-
}
59-
}
60-
if u.Mail != "" {
61-
if err := store(c.pools, userMailPrefix+strings.ToLower(u.Mail), u, c.userTTLSecs); err != nil {
62-
return err
63-
}
64-
}
65-
if u.DisplayName != "" {
66-
nameKey := userNamePrefix +
67-
strings.ToLower(u.Id.OpaqueId) + "_" +
68-
strings.ReplaceAll(strings.ToLower(u.DisplayName), " ", "_")
69-
if err := store(c.pools, nameKey, u, c.userTTLSecs); err != nil {
70-
return err
71-
}
72-
}
73-
return nil
55+
if err := store(c.pools, userIDPrefix+strings.ToLower(u.Id.OpaqueId), u, c.userTTLSecs); err != nil {
56+
return err
57+
}
58+
if u.Username != "" {
59+
if err := store(c.pools, userUsernamePrefix+strings.ToLower(u.Username), u, c.userTTLSecs); err != nil {
60+
return err
61+
}
62+
}
63+
if u.Mail != "" {
64+
if err := store(c.pools, userMailPrefix+strings.ToLower(u.Mail), u, c.userTTLSecs); err != nil {
65+
return err
66+
}
67+
}
68+
if u.DisplayName != "" {
69+
nameKey := userNamePrefix +
70+
strings.ToLower(u.Id.OpaqueId) + "_" +
71+
strings.ReplaceAll(strings.ToLower(u.DisplayName), " ", "_")
72+
if err := store(c.pools, nameKey, u, c.userTTLSecs); err != nil {
73+
return err
74+
}
75+
}
76+
return nil
7477
}
7578

7679
// GetByID looks up a user by their OpaqueId.
7780
func (c *UserCache) GetByID(ctx context.Context, opaqueID string) (*userpb.User, error) {
78-
var u userpb.User
79-
if err := fetch(ctx, c.pools, userIDPrefix+strings.ToLower(opaqueID), &u); err != nil {
80-
return nil, errtypes.NotFound(opaqueID)
81-
}
82-
return &u, nil
81+
var u userpb.User
82+
if err := fetch(ctx, c.pools, userIDPrefix+strings.ToLower(opaqueID), &u); err != nil {
83+
return nil, errtypes.NotFound(opaqueID)
84+
}
85+
return &u, nil
8386
}
8487

8588
// GetByUsername looks up a user by their login name.
8689
func (c *UserCache) GetByUsername(ctx context.Context, username string) (*userpb.User, error) {
87-
var u userpb.User
88-
if err := fetch(ctx, c.pools, userUsernamePrefix+strings.ToLower(username), &u); err != nil {
89-
return nil, errtypes.NotFound(username)
90-
}
91-
return &u, nil
90+
var u userpb.User
91+
if err := fetch(ctx, c.pools, userUsernamePrefix+strings.ToLower(username), &u); err != nil {
92+
return nil, errtypes.NotFound(username)
93+
}
94+
return &u, nil
9295
}
9396

9497
// GetByMail looks up a user by their primary email address.
9598
func (c *UserCache) GetByMail(ctx context.Context, mail string) (*userpb.User, error) {
96-
var u userpb.User
97-
if err := fetch(ctx, c.pools, userMailPrefix+strings.ToLower(mail), &u); err != nil {
98-
return nil, errtypes.NotFound(mail)
99-
}
100-
return &u, nil
99+
var u userpb.User
100+
if err := fetch(ctx, c.pools, userMailPrefix+strings.ToLower(mail), &u); err != nil {
101+
return nil, errtypes.NotFound(mail)
102+
}
103+
return &u, nil
101104
}
102105

103106
// Find scans all user index keys matching query and returns the deduplicated
104107
// set of users. An empty query returns all cached users.
105108
func (c *UserCache) Find(ctx context.Context, query string) ([]*userpb.User, error) {
106-
pattern := fmt.Sprintf(
107-
"user:*%s*",
108-
strings.ReplaceAll(strings.ToLower(query), " ", "_"),
109-
)
110-
values, err := scanAndFetch(ctx, c.pools, pattern)
111-
if err != nil {
112-
return nil, err
113-
}
114-
115-
seen := make(map[string]*userpb.User)
116-
for _, s := range values {
117-
var u userpb.User
118-
if err := json.Unmarshal([]byte(s), &u); err == nil && u.Id != nil && u.Id.OpaqueId != "" {
119-
seen[u.Id.OpaqueId] = &u
120-
}
121-
}
122-
123-
result := make([]*userpb.User, 0, len(seen))
124-
for _, u := range seen {
125-
result = append(result, u)
126-
}
127-
appctx.GetLogger(ctx).Debug().Str("pattern", pattern).Int("results", len(result)).Msg("cache: Find users")
128-
return result, nil
109+
pattern := fmt.Sprintf(
110+
"user:*%s*",
111+
strings.ReplaceAll(strings.ToLower(query), " ", "_"),
112+
)
113+
values, err := scanAndFetch(ctx, c.pools, pattern)
114+
if err != nil {
115+
return nil, err
116+
}
117+
118+
seen := make(map[string]*userpb.User)
119+
for _, s := range values {
120+
var u userpb.User
121+
if err := json.Unmarshal([]byte(s), &u); err == nil && u.Id != nil && u.Id.OpaqueId != "" {
122+
seen[u.Id.OpaqueId] = &u
123+
}
124+
}
125+
126+
result := make([]*userpb.User, 0, len(seen))
127+
for _, u := range seen {
128+
result = append(result, u)
129+
}
130+
appctx.GetLogger(ctx).Debug().Str("pattern", pattern).Int("results", len(result)).Msg("cache: Find users")
131+
return result, nil
129132
}
130133

131134
// StoreGroups writes the group membership list for a user.
132135
func (c *UserCache) StoreGroups(uid *userpb.UserId, groups []string) error {
133-
return store(c.pools, userGroupsPrefix+strings.ToLower(uid.OpaqueId), groups, c.groupsTTLSecs)
136+
return store(c.pools, userGroupsPrefix+strings.ToLower(uid.OpaqueId), groups, c.groupsTTLSecs)
134137
}
135138

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

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

0 commit comments

Comments
 (0)