Adding test case for new endpoint and covering public, builder and normal roles.

This commit is contained in:
mike12345567 2023-09-27 15:46:04 +01:00
parent bb2892cbc2
commit e916178046
8 changed files with 73 additions and 29 deletions

View File

@ -253,7 +253,10 @@ export function checkForRoleResourceArray(
* Given an app ID this will retrieve all of the roles that are currently within that app. * Given an app ID this will retrieve all of the roles that are currently within that app.
* @return {Promise<object[]>} An array of the role objects that were found. * @return {Promise<object[]>} An array of the role objects that were found.
*/ */
export async function getAllRoles(appId?: string): Promise<RoleDoc[]> { export async function getAllRoles(
appId?: string,
opts?: { idOnly: boolean }
): Promise<RoleDoc[]> {
if (appId) { if (appId) {
return doWithDB(appId, internal) return doWithDB(appId, internal)
} else { } else {
@ -308,7 +311,7 @@ export async function getAllRoles(appId?: string): Promise<RoleDoc[]> {
) )
} }
} }
return roles return opts?.idOnly ? roles.map(role => role._id) : roles
} }
} }

View File

@ -4,15 +4,14 @@
import { Heading, Icon, clickOutside } from "@budibase/bbui" import { Heading, Icon, clickOutside } from "@budibase/bbui"
import { FieldTypes } from "constants" import { FieldTypes } from "constants"
import active from "svelte-spa-router/active" import active from "svelte-spa-router/active"
import { RoleUtils } from "@budibase/frontend-core"
const sdk = getContext("sdk") const sdk = getContext("sdk")
const { const {
routeStore, routeStore,
roleStore,
styleable, styleable,
linkable, linkable,
builderStore, builderStore,
currentRole,
sidePanelStore, sidePanelStore,
} = sdk } = sdk
const component = getContext("component") const component = getContext("component")
@ -61,7 +60,8 @@
}) })
setContext("layout", store) setContext("layout", store)
$: validLinks = getValidLinks(links, $currentRole) $: accessibleRoles = $roleStore
$: validLinks = getValidLinks(links)
$: typeClass = NavigationClasses[navigation] || NavigationClasses.None $: typeClass = NavigationClasses[navigation] || NavigationClasses.None
$: navWidthClass = WidthClasses[navWidth || width] || WidthClasses.Large $: navWidthClass = WidthClasses[navWidth || width] || WidthClasses.Large
$: pageWidthClass = WidthClasses[pageWidth || width] || WidthClasses.Large $: pageWidthClass = WidthClasses[pageWidth || width] || WidthClasses.Large
@ -99,14 +99,12 @@
} }
} }
const getValidLinks = (allLinks, role) => { const getValidLinks = allLinks => {
// Strip links missing required info // Strip links missing required info
let validLinks = (allLinks || []).filter(link => link.text && link.url) let validLinks = (allLinks || []).filter(link => link.text && link.url)
// Filter to only links allowed by the current role // Filter to only links allowed by the current role
const priority = RoleUtils.getRolePriority(role)
return validLinks.filter(link => { return validLinks.filter(link => {
return !link.roleId || RoleUtils.getRolePriority(link.roleId) <= priority return accessibleRoles?.find(roleId => roleId === link.roleId)
}) })
} }

View File

@ -13,6 +13,7 @@ import {
sidePanelStore, sidePanelStore,
dndIsDragging, dndIsDragging,
confirmationStore, confirmationStore,
roleStore,
} from "stores" } from "stores"
import { styleable } from "utils/styleable" import { styleable } from "utils/styleable"
import { linkable } from "utils/linkable" import { linkable } from "utils/linkable"
@ -39,6 +40,7 @@ export default {
dndIsDragging, dndIsDragging,
currentRole, currentRole,
confirmationStore, confirmationStore,
roleStore,
styleable, styleable,
linkable, linkable,
getAction, getAction,

View File

@ -1,6 +1,7 @@
import { context, db as dbCore, events, roles } from "@budibase/backend-core" import { context, db as dbCore, events, roles } from "@budibase/backend-core"
import { getUserMetadataParams, InternalTables } from "../../db/utils" import { getUserMetadataParams, InternalTables } from "../../db/utils"
import { Database, Role, UserCtx, UserRoles } from "@budibase/types" import { Database, Role, UserCtx, UserRoles } from "@budibase/types"
import { sdk as sharedSdk } from "@budibase/shared-core"
import sdk from "../../sdk" import sdk from "../../sdk"
const UpdateRolesOptions = { const UpdateRolesOptions = {
@ -128,12 +129,18 @@ export async function destroy(ctx: UserCtx) {
role.version role.version
) )
ctx.message = `Role ${ctx.params.roleId} deleted successfully` ctx.message = `Role ${ctx.params.roleId} deleted successfully`
ctx.status = 200
} }
export async function accessible(ctx: UserCtx) { export async function accessible(ctx: UserCtx) {
const user = ctx.user let roleId = ctx.user?.roleId
if (!user || !user.roleId) { if (!roleId) {
ctx.throw(400, "User does not have a valid role") roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
}
if (ctx.user && sharedSdk.users.isAdminOrBuilder(ctx.user)) {
const appId = context.getAppId()
ctx.body = await roles.getAllRoles(appId, { idOnly: true })
} else {
ctx.body = await roles.getUserRoleHierarchy(roleId!, { idOnly: true })
} }
ctx.body = await roles.getUserRoleHierarchy(user.roleId!)
} }

View File

@ -199,8 +199,9 @@ export const serveBuilderPreview = async function (ctx: any) {
export const serveClientLibrary = async function (ctx: any) { export const serveClientLibrary = async function (ctx: any) {
let rootPath = join(NODE_MODULES_PATH, "@budibase", "client", "dist") let rootPath = join(NODE_MODULES_PATH, "@budibase", "client", "dist")
if (!fs.existsSync(rootPath)) { // incase running from TS directly
rootPath = join(__dirname, "..", "..", "..", "..", "client") if (env.isDev() && !fs.existsSync(rootPath)) {
rootPath = join(require.resolve("@budibase/client"), "..")
} }
return send(ctx, "budibase-client.js", { return send(ctx, "budibase-client.js", {
root: rootPath, root: rootPath,

View File

@ -3,17 +3,13 @@ import * as controller from "../controllers/role"
import authorized from "../../middleware/authorized" import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core" import { permissions } from "@budibase/backend-core"
import { roleValidator } from "./utils/validators" import { roleValidator } from "./utils/validators"
import { PermissionLevel, PermissionType } from "@budibase/types"
const router: Router = new Router() const router: Router = new Router()
router router
// retrieve a list of the roles a user can access // retrieve a list of the roles a user can access
.get( // needs to be public for public screens
"/api/roles/accessible", .get("/api/roles/accessible", controller.accessible)
authorized(PermissionType.APP, PermissionLevel.READ),
controller.accessible
)
.post( .post(
"/api/roles", "/api/roles",
authorized(permissions.BUILDER), authorized(permissions.BUILDER),

View File

@ -15,7 +15,7 @@ describe("/roles", () => {
await config.init() await config.init()
}) })
const createRole = async (role) => { const createRole = async role => {
if (!role) { if (!role) {
role = basicRole() role = basicRole()
} }
@ -33,9 +33,6 @@ describe("/roles", () => {
const role = basicRole() const role = basicRole()
const res = await createRole(role) const res = await createRole(role)
expect(res.res.statusMessage).toEqual(
`Role '${role.name}' created successfully.`
)
expect(res.body._id).toBeDefined() expect(res.body._id).toBeDefined()
expect(res.body._rev).toBeDefined() expect(res.body._rev).toBeDefined()
expect(events.role.updated).not.toBeCalled() expect(events.role.updated).not.toBeCalled()
@ -51,9 +48,6 @@ describe("/roles", () => {
jest.clearAllMocks() jest.clearAllMocks()
res = await createRole(res.body) res = await createRole(res.body)
expect(res.res.statusMessage).toEqual(
`Role '${role.name}' created successfully.`
)
expect(res.body._id).toBeDefined() expect(res.body._id).toBeDefined()
expect(res.body._rev).toBeDefined() expect(res.body._rev).toBeDefined()
expect(events.role.created).not.toBeCalled() expect(events.role.created).not.toBeCalled()
@ -99,7 +93,11 @@ describe("/roles", () => {
it("should be able to get the role with a permission added", async () => { it("should be able to get the role with a permission added", async () => {
const table = await config.createTable() const table = await config.createTable()
await config.api.permission.set({ roleId: BUILTIN_ROLE_IDS.POWER, resourceId: table._id, level: PermissionLevel.READ }) await config.api.permission.set({
roleId: BUILTIN_ROLE_IDS.POWER,
resourceId: table._id,
level: PermissionLevel.READ,
})
const res = await request const res = await request
.get(`/api/roles`) .get(`/api/roles`)
.set(config.defaultHeaders()) .set(config.defaultHeaders())
@ -131,4 +129,34 @@ describe("/roles", () => {
expect(events.role.deleted).toBeCalledWith(customRole) expect(events.role.deleted).toBeCalledWith(customRole)
}) })
}) })
describe("accessible", () => {
it("should be able to fetch accessible roles (with builder)", async () => {
const res = await request
.get("/api/roles/accessible")
.set(config.defaultHeaders())
.expect(200)
expect(res.body.length).toBe(5)
expect(typeof res.body[0]).toBe("string")
})
it("should be able to fetch accessible roles (basic user)", async () => {
const res = await request
.get("/api/roles/accessible")
.set(await config.basicRoleHeaders())
.expect(200)
expect(res.body.length).toBe(2)
expect(res.body[0]).toBe("BASIC")
expect(res.body[1]).toBe("PUBLIC")
})
it("should be able to fetch accessible roles (no user)", async () => {
const res = await request
.get("/api/roles/accessible")
.set(config.publicHeaders())
.expect(200)
expect(res.body.length).toBe(1)
expect(res.body[0]).toBe("PUBLIC")
})
})
}) })

View File

@ -425,6 +425,15 @@ class TestConfiguration {
return headers return headers
} }
async basicRoleHeaders() {
return await this.roleHeaders({
email: this.defaultUserValues.email,
builder: false,
prodApp: true,
roleId: roles.BUILTIN_ROLE_IDS.BASIC,
})
}
async roleHeaders({ async roleHeaders({
email = this.defaultUserValues.email, email = this.defaultUserValues.email,
roleId = roles.BUILTIN_ROLE_IDS.ADMIN, roleId = roles.BUILTIN_ROLE_IDS.ADMIN,