From 04a2bbc61aba8016d84b649273a60dc3c7c6effb Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 20 Oct 2023 15:45:35 +0100 Subject: [PATCH 1/2] Two fixes here - a quick fix for the builder side panel, making sure it fills up with users correctly (not all, but enough to make it look more pleasant) as well as dropping user search endpoint permissions to allow basic users to access it for user columns. --- packages/backend-core/src/users/db.ts | 10 +++++----- packages/backend-core/src/users/users.ts | 9 ++++++--- .../_components/BuilderSidePanel.svelte | 3 ++- packages/types/src/api/web/user.ts | 1 + .../worker/src/api/controllers/global/users.ts | 7 ++++++- .../src/api/routes/global/tests/users.spec.ts | 6 +++++- packages/worker/src/api/routes/global/users.ts | 3 ++- packages/worker/src/tests/api/users.ts | 14 ++++++++++---- 8 files changed, 37 insertions(+), 16 deletions(-) diff --git a/packages/backend-core/src/users/db.ts b/packages/backend-core/src/users/db.ts index 8bb6300d4ea..daa09bee6f6 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 a64997224ec..bad108ab84e 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 c93a41f5418..a7d9584330d 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 a1e039cfd79..3a5bd16bdf4 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 8de3a1444e2..bfe9bdf284a 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 a446d10ed03..846b98a7ae9 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 a57f7834ac0..3c9cfd2f41d 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 b2a19bcb287..ca25e2f9ca6 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) => { From 8c744ea7a99ab68e7388094ab74c6fc5ba708e78 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 20 Oct 2023 15:57:45 +0100 Subject: [PATCH 2/2] PR comments. --- packages/worker/src/api/controllers/global/users.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index bfe9bdf284a..de1a6058906 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -206,10 +206,8 @@ 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) {