11package cache
22
33import (
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
2222const (
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.
3234type 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
4245func 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.
5154func (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.
7780func (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.
8689func (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.
9598func (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.
105108func (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.
132135func (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).
138141func (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}
0 commit comments