diff --git a/packages/backend-core/src/users/db.ts b/packages/backend-core/src/users/db.ts index 8bb6300d4e..daa09bee6f 100644 --- a/packages/backend-core/src/users/db.ts +++ b/packages/backend-core/src/users/db.ts @@ -164,14 +164,14 @@ export class UserDB { } } - static async getUsersByAppAccess(appId?: string) { - const opts: any = { + static async getUsersByAppAccess(opts: { appId?: string; limit?: number }) { + const params: any = { include_docs: true, - limit: 50, + limit: opts.limit || 50, } let response: User[] = await usersCore.searchGlobalUsersByAppAccess( - appId, - opts + opts.appId, + params ) return response } diff --git a/packages/backend-core/src/users/users.ts b/packages/backend-core/src/users/users.ts index a64997224e..bad108ab84 100644 --- a/packages/backend-core/src/users/users.ts +++ b/packages/backend-core/src/users/users.ts @@ -19,6 +19,7 @@ import { SearchUsersRequest, User, ContextUser, + DatabaseQueryOpts, } from "@budibase/types" import { getGlobalDB } from "../context" import * as context from "../context" @@ -241,12 +242,14 @@ export const paginatedUsers = async ({ bookmark, query, appId, + limit, }: SearchUsersRequest = {}) => { const db = getGlobalDB() + const pageLimit = limit ? limit + 1 : PAGE_LIMIT + 1 // get one extra document, to have the next page - const opts: any = { + const opts: DatabaseQueryOpts = { include_docs: true, - limit: PAGE_LIMIT + 1, + limit: pageLimit, } // add a startkey if the page was specified (anchor) if (bookmark) { @@ -269,7 +272,7 @@ export const paginatedUsers = async ({ const response = await db.allDocs(getGlobalUserParams(null, opts)) userList = response.rows.map((row: any) => row.doc) } - return pagination(userList, PAGE_LIMIT, { + return pagination(userList, pageLimit, { paginate: true, property, getKey, diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte index c93a41f541..a7d9584330 100644 --- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte @@ -114,8 +114,9 @@ query: { appId: query || !filterByAppAccess ? null : prodAppId, email: query, - paginated: query || !filterByAppAccess ? null : false, }, + limit: 50, + paginate: query || !filterByAppAccess ? null : false, }) await usersFetch.refresh() diff --git a/packages/types/src/api/web/user.ts b/packages/types/src/api/web/user.ts index a1e039cfd7..3a5bd16bdf 100644 --- a/packages/types/src/api/web/user.ts +++ b/packages/types/src/api/web/user.ts @@ -55,6 +55,7 @@ export interface SearchUsersRequest { bookmark?: string query?: SearchQuery appId?: string + limit?: number paginate?: boolean } diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index 8de3a1444e..bfe9bdf284 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -189,7 +189,10 @@ export const destroy = async (ctx: any) => { export const getAppUsers = async (ctx: Ctx) => { const body = ctx.request.body - const users = await userSdk.db.getUsersByAppAccess(body?.appId) + const users = await userSdk.db.getUsersByAppAccess({ + appId: body.appId, + limit: body.limit, + }) ctx.body = { data: users } } @@ -203,8 +206,10 @@ export const search = async (ctx: Ctx) => { } if (body.paginate === false) { + console.log("not paginated") await getAppUsers(ctx) } else { + console.log("paginated") const paginated = await userSdk.core.paginatedUsers(body) // user hashed password shouldn't ever be returned for (let user of paginated.data) { diff --git a/packages/worker/src/api/routes/global/tests/users.spec.ts b/packages/worker/src/api/routes/global/tests/users.spec.ts index a446d10ed0..846b98a7ae 100644 --- a/packages/worker/src/api/routes/global/tests/users.spec.ts +++ b/packages/worker/src/api/routes/global/tests/users.spec.ts @@ -569,9 +569,13 @@ describe("/api/global/users", () => { { query: { equal: { firstName: user.firstName } }, }, - 501 + { status: 501 } ) }) + + it("should throw an error if public query performed", async () => { + await config.api.users.searchUsers({}, { status: 403, noHeaders: true }) + }) }) describe("DELETE /api/global/users/:userId", () => { diff --git a/packages/worker/src/api/routes/global/users.ts b/packages/worker/src/api/routes/global/users.ts index a57f7834ac..3c9cfd2f41 100644 --- a/packages/worker/src/api/routes/global/users.ts +++ b/packages/worker/src/api/routes/global/users.ts @@ -72,7 +72,8 @@ router ) .get("/api/global/users", auth.builderOrAdmin, controller.fetch) - .post("/api/global/users/search", auth.builderOrAdmin, controller.search) + // search can be used by any user now, to retrieve users for user column + .post("/api/global/users/search", controller.search) .delete("/api/global/users/:id", auth.adminOnly, controller.destroy) .get( "/api/global/users/count/:appId", diff --git a/packages/worker/src/tests/api/users.ts b/packages/worker/src/tests/api/users.ts index b2a19bcb28..ca25e2f9ca 100644 --- a/packages/worker/src/tests/api/users.ts +++ b/packages/worker/src/tests/api/users.ts @@ -134,13 +134,19 @@ export class UserAPI extends TestAPI { .expect(status ? status : 200) } - searchUsers = ({ query }: { query?: SearchQuery }, status = 200) => { - return this.request + searchUsers = ( + { query }: { query?: SearchQuery }, + opts?: { status?: number; noHeaders?: boolean } + ) => { + const req = this.request .post("/api/global/users/search") - .set(this.config.defaultHeaders()) .send({ query }) .expect("Content-Type", /json/) - .expect(status ? status : 200) + .expect(opts?.status ? opts.status : 200) + if (!opts?.noHeaders) { + req.set(this.config.defaultHeaders()) + } + return req } getUser = (userId: string, opts?: TestAPIOpts) => {