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.

This commit is contained in:
mike12345567 2023-10-20 15:45:35 +01:00
parent 0010e07b47
commit 04a2bbc61a
8 changed files with 37 additions and 16 deletions

View File

@ -164,14 +164,14 @@ export class UserDB {
} }
} }
static async getUsersByAppAccess(appId?: string) { static async getUsersByAppAccess(opts: { appId?: string; limit?: number }) {
const opts: any = { const params: any = {
include_docs: true, include_docs: true,
limit: 50, limit: opts.limit || 50,
} }
let response: User[] = await usersCore.searchGlobalUsersByAppAccess( let response: User[] = await usersCore.searchGlobalUsersByAppAccess(
appId, opts.appId,
opts params
) )
return response return response
} }

View File

@ -19,6 +19,7 @@ import {
SearchUsersRequest, SearchUsersRequest,
User, User,
ContextUser, ContextUser,
DatabaseQueryOpts,
} from "@budibase/types" } from "@budibase/types"
import { getGlobalDB } from "../context" import { getGlobalDB } from "../context"
import * as context from "../context" import * as context from "../context"
@ -241,12 +242,14 @@ export const paginatedUsers = async ({
bookmark, bookmark,
query, query,
appId, appId,
limit,
}: SearchUsersRequest = {}) => { }: SearchUsersRequest = {}) => {
const db = getGlobalDB() const db = getGlobalDB()
const pageLimit = limit ? limit + 1 : PAGE_LIMIT + 1
// get one extra document, to have the next page // get one extra document, to have the next page
const opts: any = { const opts: DatabaseQueryOpts = {
include_docs: true, include_docs: true,
limit: PAGE_LIMIT + 1, limit: pageLimit,
} }
// add a startkey if the page was specified (anchor) // add a startkey if the page was specified (anchor)
if (bookmark) { if (bookmark) {
@ -269,7 +272,7 @@ export const paginatedUsers = async ({
const response = await db.allDocs(getGlobalUserParams(null, opts)) const response = await db.allDocs(getGlobalUserParams(null, opts))
userList = response.rows.map((row: any) => row.doc) userList = response.rows.map((row: any) => row.doc)
} }
return pagination(userList, PAGE_LIMIT, { return pagination(userList, pageLimit, {
paginate: true, paginate: true,
property, property,
getKey, getKey,

View File

@ -114,8 +114,9 @@
query: { query: {
appId: query || !filterByAppAccess ? null : prodAppId, appId: query || !filterByAppAccess ? null : prodAppId,
email: query, email: query,
paginated: query || !filterByAppAccess ? null : false,
}, },
limit: 50,
paginate: query || !filterByAppAccess ? null : false,
}) })
await usersFetch.refresh() await usersFetch.refresh()

View File

@ -55,6 +55,7 @@ export interface SearchUsersRequest {
bookmark?: string bookmark?: string
query?: SearchQuery query?: SearchQuery
appId?: string appId?: string
limit?: number
paginate?: boolean paginate?: boolean
} }

View File

@ -189,7 +189,10 @@ export const destroy = async (ctx: any) => {
export const getAppUsers = async (ctx: Ctx<SearchUsersRequest>) => { export const getAppUsers = async (ctx: Ctx<SearchUsersRequest>) => {
const body = ctx.request.body 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 } ctx.body = { data: users }
} }
@ -203,8 +206,10 @@ export const search = async (ctx: Ctx<SearchUsersRequest>) => {
} }
if (body.paginate === false) { if (body.paginate === false) {
console.log("not paginated")
await getAppUsers(ctx) await getAppUsers(ctx)
} else { } else {
console.log("paginated")
const paginated = await userSdk.core.paginatedUsers(body) const paginated = await userSdk.core.paginatedUsers(body)
// user hashed password shouldn't ever be returned // user hashed password shouldn't ever be returned
for (let user of paginated.data) { for (let user of paginated.data) {

View File

@ -569,9 +569,13 @@ describe("/api/global/users", () => {
{ {
query: { equal: { firstName: user.firstName } }, 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", () => { describe("DELETE /api/global/users/:userId", () => {

View File

@ -72,7 +72,8 @@ router
) )
.get("/api/global/users", auth.builderOrAdmin, controller.fetch) .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) .delete("/api/global/users/:id", auth.adminOnly, controller.destroy)
.get( .get(
"/api/global/users/count/:appId", "/api/global/users/count/:appId",

View File

@ -134,13 +134,19 @@ export class UserAPI extends TestAPI {
.expect(status ? status : 200) .expect(status ? status : 200)
} }
searchUsers = ({ query }: { query?: SearchQuery }, status = 200) => { searchUsers = (
return this.request { query }: { query?: SearchQuery },
opts?: { status?: number; noHeaders?: boolean }
) => {
const req = this.request
.post("/api/global/users/search") .post("/api/global/users/search")
.set(this.config.defaultHeaders())
.send({ query }) .send({ query })
.expect("Content-Type", /json/) .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) => { getUser = (userId: string, opts?: TestAPIOpts) => {