From 1e34429869ca21a4ba27113688100c4d76beb191 Mon Sep 17 00:00:00 2001 From: Simon Esposito Date: Fri, 8 Sep 2023 16:54:15 +0100 Subject: [PATCH] Add storageList caller id param --- server/runtime_go_nakama.go | 44 +++++++++++++++---- server/runtime_javascript_nakama.go | 29 +++++++++++- server/runtime_lua_nakama.go | 39 ++++++++++++---- server/storage_index.go | 10 +++-- .../nakama-common/runtime/runtime.go | 4 +- 5 files changed, 102 insertions(+), 24 deletions(-) diff --git a/server/runtime_go_nakama.go b/server/runtime_go_nakama.go index f0dce4f7bf..f3f4579af9 100644 --- a/server/runtime_go_nakama.go +++ b/server/runtime_go_nakama.go @@ -1867,6 +1867,7 @@ func (n *RuntimeGoNakamaModule) WalletLedgerList(ctx context.Context, userID str // @group storage // @summary List records in a collection and page through results. The records returned can be filtered to those owned by the user or "" for public records. // @param ctx(type=context.Context) The context object represents information about the server and requester. +// @param callerId(type=string) User ID of the caller, will apply permissions checks of the user. If empty defaults to system user and permissions are bypassed. // @param userId(type=string) User ID to list records for or "" (empty string) for public records. // @param collection(type=string) Collection to list data from. // @param limit(type=int, optional=true, default=100) Limit number of records retrieved. @@ -1874,7 +1875,16 @@ func (n *RuntimeGoNakamaModule) WalletLedgerList(ctx context.Context, userID str // @return objects([]*api.StorageObject) A list of storage objects. // @return cursor(string) Pagination cursor. Will be set to "" or nil when fetching last available page. // @return error(error) An optional error value if an error occurred. -func (n *RuntimeGoNakamaModule) StorageList(ctx context.Context, userID, collection string, limit int, cursor string) ([]*api.StorageObject, string, error) { +func (n *RuntimeGoNakamaModule) StorageList(ctx context.Context, callerID, userID, collection string, limit int, cursor string) ([]*api.StorageObject, string, error) { + cid := uuid.Nil + if callerID != "" { + u, err := uuid.FromString(callerID) + if err != nil { + return nil, "", errors.New("expects an empty or valid caller id") + } + cid = u + } + var uid *uuid.UUID if userID != "" { u, err := uuid.FromString(userID) @@ -1888,7 +1898,7 @@ func (n *RuntimeGoNakamaModule) StorageList(ctx context.Context, userID, collect return nil, "", errors.New("limit must not be negative") } - objectList, _, err := StorageListObjects(ctx, n.logger, n.db, uuid.Nil, uid, collection, limit, cursor) + objectList, _, err := StorageListObjects(ctx, n.logger, n.db, cid, uid, collection, limit, cursor) if err != nil { return nil, "", err } @@ -2047,19 +2057,30 @@ func (n *RuntimeGoNakamaModule) StorageDelete(ctx context.Context, deletes []*ru // @group storage // @summary List storage index entries // @param indexName(type=string) Name of the index to list entries from. +// @param callerId(type=string) User ID of the caller, will apply permissions checks of the user. If empty defaults to system user and permissions are bypassed. // @param queryString(type=string) Query to filter index entries. // @param limit(type=int) Maximum number of results to be returned. // @return objects(*api..StorageObjectList) A list of storage objects. // @return error(error) An optional error value if an error occurred. -func (n *RuntimeGoNakamaModule) StorageIndexList(ctx context.Context, indexName, query string, limit int) (*api.StorageObjects, error) { +func (n *RuntimeGoNakamaModule) StorageIndexList(ctx context.Context, callerID, indexName, query string, limit int) (*api.StorageObjects, error) { + cid := uuid.Nil + if callerID != "" { + id, err := uuid.FromString(callerID) + if err != nil { + return nil, errors.New("expects caller id to be empty or a valid user id") + } + cid = id + } + if indexName == "" { return nil, errors.New("expects a non-empty indexName") } + if limit < 1 || limit > 100 { return nil, errors.New("limit must be 1-100") } - return n.storageIndex.List(ctx, indexName, query, limit) + return n.storageIndex.List(ctx, cid, indexName, query, limit) } // @group users @@ -3297,6 +3318,7 @@ func (n *RuntimeGoNakamaModule) GroupUserLeave(ctx context.Context, groupID, use // @group groups // @summary Add users to a group. // @param ctx(type=context.Context) The context object represents information about the server and requester. +// @param callerId(type=string) User ID of the caller, will apply permissions checks of the user. If empty defaults to system user and permissions are bypassed. // @param groupId(type=string) The ID of the group to add users to. // @param userIds(type=[]string) Table array of user IDs to add to this group. // @return error(error) An optional error value if an error occurred. @@ -3305,7 +3327,7 @@ func (n *RuntimeGoNakamaModule) GroupUsersAdd(ctx context.Context, callerID, gro if callerID != "" { var err error if caller, err = uuid.FromString(callerID); err != nil { - return errors.New("expects caller ID to be a valid identifier") + return errors.New("expects caller ID to be empty or a valid identifier") } } @@ -3336,6 +3358,7 @@ func (n *RuntimeGoNakamaModule) GroupUsersAdd(ctx context.Context, callerID, gro // @group groups // @summary Ban users from a group. // @param ctx(type=context.Context) The context object represents information about the server and requester. +// @param callerId(type=string) User ID of the caller, will apply permissions checks of the user. If empty defaults to system user and permissions are bypassed. // @param groupId(type=string) The ID of the group to ban users from. // @param userIds(type=[]string) Table array of user IDs to ban from this group. // @return error(error) An optional error value if an error occurred. @@ -3344,7 +3367,7 @@ func (n *RuntimeGoNakamaModule) GroupUsersBan(ctx context.Context, callerID, gro if callerID != "" { var err error if caller, err = uuid.FromString(callerID); err != nil { - return errors.New("expects caller ID to be a valid identifier") + return errors.New("expects caller ID to be empty or a valid identifier") } } @@ -3375,6 +3398,7 @@ func (n *RuntimeGoNakamaModule) GroupUsersBan(ctx context.Context, callerID, gro // @group groups // @summary Kick users from a group. // @param ctx(type=context.Context) The context object represents information about the server and requester. +// @param callerId(type=string) User ID of the caller, will apply permissions checks of the user. If empty defaults to system user and permissions are bypassed. // @param groupId(type=string) The ID of the group to kick users from. // @param userIds(type=[]string) Table array of user IDs to kick. // @return error(error) An optional error value if an error occurred. @@ -3383,7 +3407,7 @@ func (n *RuntimeGoNakamaModule) GroupUsersKick(ctx context.Context, callerID, gr if callerID != "" { var err error if caller, err = uuid.FromString(callerID); err != nil { - return errors.New("expects caller ID to be a valid identifier") + return errors.New("expects caller ID to be empty or a valid identifier") } } @@ -3414,6 +3438,7 @@ func (n *RuntimeGoNakamaModule) GroupUsersKick(ctx context.Context, callerID, gr // @group groups // @summary Promote users in a group. // @param ctx(type=context.Context) The context object represents information about the server and requester. +// @param callerId(type=string) User ID of the caller, will apply permissions checks of the user. If empty defaults to system user and permissions are bypassed. // @param groupId(type=string) The ID of the group whose members are being promoted. // @param userIds(type=[]string) Table array of user IDs to promote. // @return error(error) An optional error value if an error occurred. @@ -3422,7 +3447,7 @@ func (n *RuntimeGoNakamaModule) GroupUsersPromote(ctx context.Context, callerID, if callerID != "" { var err error if caller, err = uuid.FromString(callerID); err != nil { - return errors.New("expects caller ID to be a valid identifier") + return errors.New("expects caller ID to be empty or a valid identifier") } } @@ -3453,6 +3478,7 @@ func (n *RuntimeGoNakamaModule) GroupUsersPromote(ctx context.Context, callerID, // @group groups // @summary Demote users in a group. // @param ctx(type=context.Context) The context object represents information about the server and requester. +// @param callerId(type=string) User ID of the caller, will apply permissions checks of the user. If empty defaults to system user and permissions are bypassed. // @param groupId(type=string) The ID of the group whose members are being demoted. // @param userIds(type=[]string) Table array of user IDs to demote. // @return error(error) An optional error value if an error occurred. @@ -3461,7 +3487,7 @@ func (n *RuntimeGoNakamaModule) GroupUsersDemote(ctx context.Context, callerID, if callerID != "" { var err error if caller, err = uuid.FromString(callerID); err != nil { - return errors.New("expects caller ID to be a valid identifier") + return errors.New("expects caller ID to be empty or a valid identifier") } } diff --git a/server/runtime_javascript_nakama.go b/server/runtime_javascript_nakama.go index 233ae271b1..c06d04c818 100644 --- a/server/runtime_javascript_nakama.go +++ b/server/runtime_javascript_nakama.go @@ -345,6 +345,7 @@ func (n *runtimeJavascriptNakamaModule) stringToBinary(r *goja.Runtime) func(goj // @param indexName(type=string) Name of the index to list entries from. // @param queryString(type=string) Query to filter index entries. // @param limit(type=int) Maximum number of results to be returned. +// @param callerId(type=string) Optional User ID of the caller, will apply permissions checks of the user. If empty defaults to system user and permission checks are bypassed. // @return objects(nkruntime.StorageObjectList) A list of storage objects. // @return error(error) An optional error value if an error occurred. func (n *runtimeJavascriptNakamaModule) storageIndexList(r *goja.Runtime) func(goja.FunctionCall) goja.Value { @@ -358,8 +359,17 @@ func (n *runtimeJavascriptNakamaModule) storageIndexList(r *goja.Runtime) func(g panic(r.NewTypeError("limit must be 1-100")) } } + callerID := uuid.Nil + if !goja.IsUndefined(f.Argument(3)) && !goja.IsNull(f.Argument(3)) { + callerIdStr := getJsString(r, f.Argument(3)) + cid, err := uuid.FromString(callerIdStr) + if err != nil { + panic(r.NewTypeError("expects caller id to be valid identifier")) + } + callerID = cid + } - objectList, err := n.storageIndex.List(n.ctx, idxName, queryString, int(limit)) + objectList, err := n.storageIndex.List(n.ctx, callerID, idxName, queryString, int(limit)) if err != nil { panic(r.NewGoError(fmt.Errorf("failed to lookup storage index: %s", err.Error()))) } @@ -4248,7 +4258,17 @@ func (n *runtimeJavascriptNakamaModule) storageList(r *goja.Runtime) func(goja.F cursor = getJsString(r, f.Argument(3)) } - objectList, _, err := StorageListObjects(n.ctx, n.logger, n.db, uuid.Nil, uid, collection, limit, cursor) + callerID := uuid.Nil + if !goja.IsUndefined(f.Argument(4)) && !goja.IsNull(f.Argument(4)) { + callerIdStr := getJsString(r, f.Argument(4)) + cid, err := uuid.FromString(callerIdStr) + if err != nil { + panic(r.NewTypeError("expects caller id to be valid identifier")) + } + callerID = cid + } + + objectList, _, err := StorageListObjects(n.ctx, n.logger, n.db, callerID, uid, collection, limit, cursor) if err != nil { panic(r.NewGoError(fmt.Errorf("failed to list storage objects: %s", err.Error()))) } @@ -6771,6 +6791,7 @@ func (n *runtimeJavascriptNakamaModule) groupDelete(r *goja.Runtime) func(goja.F // @summary Kick users from a group. // @param groupId(type=string) The ID of the group to kick users from. // @param userIds(type=string[]) Table array of user IDs to kick. +// @param callerId(type=string) Optional User ID of the caller, will apply permissions checks of the user. If empty defaults to system user and permission checks are bypassed. // @return error(error) An optional error value if an error occurred. func (n *runtimeJavascriptNakamaModule) groupUsersKick(r *goja.Runtime) func(goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { @@ -7446,6 +7467,7 @@ func (n *runtimeJavascriptNakamaModule) groupUserLeave(r *goja.Runtime) func(goj // @summary Add users to a group. // @param groupId(type=string) The ID of the group to add users to. // @param userIds(type=string[]) Table array of user IDs to add to this group. +// @param callerId(type=string) Optional User ID of the caller, will apply permissions checks of the user. If empty defaults to system user and permission checks are bypassed. // @return error(error) An optional error value if an error occurred. func (n *runtimeJavascriptNakamaModule) groupUsersAdd(r *goja.Runtime) func(goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { @@ -7508,6 +7530,7 @@ func (n *runtimeJavascriptNakamaModule) groupUsersAdd(r *goja.Runtime) func(goja // @summary Ban users from a group. // @param groupId(string) The ID of the group to ban users from. // @param userIds(string[]) Table array of user IDs to ban from this group. +// @param callerId(type=string) Optional User ID of the caller, will apply permissions checks of the user. If empty defaults to system user and permission checks are bypassed. // @return error(error) An optional error value if an error occurred. func (n *runtimeJavascriptNakamaModule) groupUsersBan(r *goja.Runtime) func(goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { @@ -7570,6 +7593,7 @@ func (n *runtimeJavascriptNakamaModule) groupUsersBan(r *goja.Runtime) func(goja // @summary Promote users in a group. // @param groupId(type=string) The ID of the group whose members are being promoted. // @param userIds(type=string[]) Table array of user IDs to promote. +// @param callerId(type=string) Optional User ID of the caller, will apply permissions checks of the user. If empty defaults to system user and permission checks are bypassed. // @return error(error) An optional error value if an error occurred. func (n *runtimeJavascriptNakamaModule) groupUsersPromote(r *goja.Runtime) func(goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { @@ -7632,6 +7656,7 @@ func (n *runtimeJavascriptNakamaModule) groupUsersPromote(r *goja.Runtime) func( // @summary Demote users in a group. // @param groupId(type=string) The ID of the group whose members are being demoted. // @param userIds(type=string[]) Table array of user IDs to demote. +// @param callerId(type=string) Optional User ID of the caller, will apply permissions checks of the user. If empty defaults to system user and permission checks are bypassed. // @return error(error) An optional error value if an error occurred. func (n *runtimeJavascriptNakamaModule) groupUsersDemote(r *goja.Runtime) func(goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { diff --git a/server/runtime_lua_nakama.go b/server/runtime_lua_nakama.go index 66bbb16f35..c1257b1738 100644 --- a/server/runtime_lua_nakama.go +++ b/server/runtime_lua_nakama.go @@ -5539,10 +5539,11 @@ func (n *RuntimeLuaNakamaModule) walletLedgerList(l *lua.LState) int { // @group storage // @summary List records in a collection and page through results. The records returned can be filtered to those owned by the user or "" for public records. -// @param userId(type=string) User ID to list records for or "" (empty string) for public records. +// @param userId(type=string) User ID to list records for or "" (empty string) | void for public records. // @param collection(type=string) Collection to list data from. // @param limit(type=number, optional=true, default=100) Limit number of records retrieved. // @param cursor(type=string, optional=true, default="") Pagination cursor from previous result. Don't set to start fetching from the beginning. +// @param callerId(type=string, optional=true) User ID of the caller, will apply permissions checks of the user. If empty defaults to system user and permission checks are bypassed. // @return objects(table) A list of storage objects. // @return cursor(string) Pagination cursor. // @return error(error) An optional error value if an error occurred. @@ -5568,7 +5569,18 @@ func (n *RuntimeLuaNakamaModule) storageList(l *lua.LState) int { userID = &uid } - objectList, _, err := StorageListObjects(l.Context(), n.logger, n.db, uuid.Nil, userID, collection, limit, cursor) + callerID := uuid.Nil + callerIDStr := l.OptString(4, "") + if callerIDStr != "" { + cid, err := uuid.FromString(callerIDStr) + if err != nil { + l.ArgError(4, "expects caller ID to be empty or a valid identifier") + return 0 + } + callerID = cid + } + + objectList, _, err := StorageListObjects(l.Context(), n.logger, n.db, callerID, userID, collection, limit, cursor) if err != nil { l.RaiseError(fmt.Sprintf("failed to list storage objects: %s", err.Error())) return 0 @@ -8453,7 +8465,7 @@ func (n *RuntimeLuaNakamaModule) groupUsersAdd(l *lua.LState) int { if callerIDStr != "" { callerID, err = uuid.FromString(callerIDStr) if err != nil { - l.ArgError(1, "expects caller ID to be a valid identifier") + l.ArgError(3, "expects caller ID to be empty or a valid identifier") return 0 } } @@ -8516,7 +8528,7 @@ func (n *RuntimeLuaNakamaModule) groupUsersBan(l *lua.LState) int { if callerIDStr != "" { callerID, err = uuid.FromString(callerIDStr) if err != nil { - l.ArgError(1, "expects caller ID to be a valid identifier") + l.ArgError(3, "expects caller ID to be empty or a valid identifier") return 0 } } @@ -8579,7 +8591,7 @@ func (n *RuntimeLuaNakamaModule) groupUsersPromote(l *lua.LState) int { if callerIDStr != "" { callerID, err = uuid.FromString(callerIDStr) if err != nil { - l.ArgError(1, "expects caller ID to be a valid identifier") + l.ArgError(3, "expects caller ID to be empty or a valid identifier") return 0 } } @@ -8642,7 +8654,7 @@ func (n *RuntimeLuaNakamaModule) groupUsersDemote(l *lua.LState) int { if callerIDStr != "" { callerID, err = uuid.FromString(callerIDStr) if err != nil { - l.ArgError(1, "expects caller ID to be a valid identifier") + l.ArgError(3, "expects caller ID to be empty or a valid identifier") return 0 } } @@ -8705,7 +8717,7 @@ func (n *RuntimeLuaNakamaModule) groupUsersKick(l *lua.LState) int { if callerIDStr != "" { callerID, err = uuid.FromString(callerIDStr) if err != nil { - l.ArgError(1, "expects caller ID to be a valid identifier") + l.ArgError(3, "expects caller ID to be empty or a valid identifier") return 0 } } @@ -9834,6 +9846,7 @@ func (n *RuntimeLuaNakamaModule) channelIdBuild(l *lua.LState) int { // @param indexName(type=string) Name of the index to list entries from. // @param queryString(type=string) Query to filter index entries. // @param limit(type=int) Maximum number of results to be returned. +// @param callerId(type=string) Optional User ID of the caller, will apply permissions checks of the user. If empty defaults to system user and permission checks are bypassed. // @return objects(table) A list of storage objects. // @return error(error) An optional error value if an error occurred. func (n *RuntimeLuaNakamaModule) storageIndexList(l *lua.LState) int { @@ -9844,8 +9857,18 @@ func (n *RuntimeLuaNakamaModule) storageIndexList(l *lua.LState) int { l.ArgError(3, "invalid limit: expects value 1-100") return 0 } + callerID := uuid.Nil + callerIDStr := l.OptString(4, "") + if callerIDStr != "" { + cid, err := uuid.FromString(callerIDStr) + if err != nil { + l.ArgError(4, "expects caller ID to be empty or a valid identifier") + return 0 + } + callerID = cid + } - objectList, err := n.storageIndex.List(l.Context(), idxName, queryString, limit) + objectList, err := n.storageIndex.List(l.Context(), callerID, idxName, queryString, limit) if err != nil { l.RaiseError(err.Error()) return 0 diff --git a/server/storage_index.go b/server/storage_index.go index bddd28901e..0ef263230d 100644 --- a/server/storage_index.go +++ b/server/storage_index.go @@ -35,7 +35,7 @@ import ( type StorageIndex interface { Write(ctx context.Context, objects []*api.StorageObject) (creates int, deletes int) Delete(ctx context.Context, objects StorageOpDeletes) (deletes int) - List(ctx context.Context, indexName, query string, limit int) (*api.StorageObjects, error) + List(ctx context.Context, callerID uuid.UUID, indexName, query string, limit int) (*api.StorageObjects, error) Load(ctx context.Context) error CreateIndex(ctx context.Context, name, collection, key string, fields []string, maxEntries int, indexOnly bool) error RegisterFilters(runtime *Runtime) @@ -211,7 +211,7 @@ func (si *LocalStorageIndex) Delete(ctx context.Context, objects StorageOpDelete return deletes } -func (si *LocalStorageIndex) List(ctx context.Context, indexName, query string, limit int) (*api.StorageObjects, error) { +func (si *LocalStorageIndex) List(ctx context.Context, callerID uuid.UUID, indexName, query string, limit int) (*api.StorageObjects, error) { idx, found := si.indexByName[indexName] if !found { return nil, fmt.Errorf("index %q not found", indexName) @@ -253,6 +253,10 @@ func (si *LocalStorageIndex) List(ctx context.Context, indexName, query string, if idx.IndexOnly { objects := make([]*api.StorageObject, 0, len(indexResults)) for _, idxResult := range indexResults { + if callerID != uuid.Nil && !(idxResult.Read == 2 || idxResult.UserID == callerID.String()) { + continue + } + objects = append(objects, &api.StorageObject{ Collection: idxResult.Collection, Key: idxResult.Key, @@ -278,7 +282,7 @@ func (si *LocalStorageIndex) List(ctx context.Context, indexName, query string, }) } - objects, err := StorageReadObjects(ctx, si.logger, si.db, uuid.Nil, storageReads) + objects, err := StorageReadObjects(ctx, si.logger, si.db, callerID, storageReads) if err != nil { return nil, err } diff --git a/vendor/github.com/heroiclabs/nakama-common/runtime/runtime.go b/vendor/github.com/heroiclabs/nakama-common/runtime/runtime.go index acd2b02f8f..c0db3a734f 100644 --- a/vendor/github.com/heroiclabs/nakama-common/runtime/runtime.go +++ b/vendor/github.com/heroiclabs/nakama-common/runtime/runtime.go @@ -1066,11 +1066,11 @@ type NakamaModule interface { WalletLedgerUpdate(ctx context.Context, itemID string, metadata map[string]interface{}) (WalletLedgerItem, error) WalletLedgerList(ctx context.Context, userID string, limit int, cursor string) ([]WalletLedgerItem, string, error) - StorageList(ctx context.Context, userID, collection string, limit int, cursor string) ([]*api.StorageObject, string, error) + StorageList(ctx context.Context, callerID, userID, collection string, limit int, cursor string) ([]*api.StorageObject, string, error) StorageRead(ctx context.Context, reads []*StorageRead) ([]*api.StorageObject, error) StorageWrite(ctx context.Context, writes []*StorageWrite) ([]*api.StorageObjectAck, error) StorageDelete(ctx context.Context, deletes []*StorageDelete) error - StorageIndexList(ctx context.Context, indexName, query string, limit int) (*api.StorageObjects, error) + StorageIndexList(ctx context.Context, callerID, indexName, query string, limit int) (*api.StorageObjects, error) MultiUpdate(ctx context.Context, accountUpdates []*AccountUpdate, storageWrites []*StorageWrite, walletUpdates []*WalletUpdate, updateLedger bool) ([]*api.StorageObjectAck, []*WalletUpdateResult, error)