From 04a2bbc61aba8016d84b649273a60dc3c7c6effb Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 20 Oct 2023 15:45:35 +0100 Subject: [PATCH] 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 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) => {