Updates to remove app builder concept, denying access to app creation for app builders.

This commit is contained in:
mike12345567 2023-07-26 17:32:21 +01:00
parent c277b065db
commit 64a5426d36
11 changed files with 42 additions and 89 deletions

View File

@ -1,29 +1,12 @@
const { flatten } = require("lodash")
const { cloneDeep } = require("lodash/fp")
import { flatten } from "lodash"
import { cloneDeep } from "lodash/fp"
import { PermissionType, PermissionLevel } from "@budibase/types"
export { PermissionType, PermissionLevel } from "@budibase/types"
export type RoleHierarchy = {
permissionId: string
}[]
export enum PermissionLevel {
READ = "read",
WRITE = "write",
EXECUTE = "execute",
ADMIN = "admin",
}
// these are the global types, that govern the underlying default behaviour
export enum PermissionType {
APP = "app",
TABLE = "table",
USER = "user",
AUTOMATION = "automation",
WEBHOOK = "webhook",
BUILDER = "builder",
VIEW = "view",
QUERY = "query",
}
export class Permission {
type: PermissionType
level: PermissionLevel
@ -173,3 +156,4 @@ export function isPermissionLevelHigherThanRead(level: PermissionLevel) {
// utility as a lot of things need simply the builder permission
export const BUILDER = PermissionType.BUILDER
export const GLOBAL_BUILDER = PermissionType.GLOBAL_BUILDER

View File

@ -15,7 +15,7 @@ router
)
.post(
"/api/applications",
authorized(permissions.BUILDER),
authorized(permissions.GLOBAL_BUILDER),
applicationValidator(),
controller.create
)

View File

@ -5,7 +5,7 @@ import {
context,
users,
} from "@budibase/backend-core"
import { Role, UserCtx } from "@budibase/types"
import { Role, UserCtx, PermissionType, PermissionLevel } from "@budibase/types"
import builderMiddleware from "./builder"
import { isWebhookEndpoint } from "./utils"
@ -24,8 +24,8 @@ const csrf = auth.buildCsrfMiddleware()
const checkAuthorized = async (
ctx: UserCtx,
resourceRoles: any,
permType: any,
permLevel: any
permType: PermissionType,
permLevel: PermissionLevel
) => {
const appId = context.getAppId()
// check if this is a builder api and the user is not a builder
@ -47,10 +47,10 @@ const checkAuthorized = async (
}
const checkAuthorizedResource = async (
ctx: any,
ctx: UserCtx,
resourceRoles: any,
permType: any,
permLevel: any
permType: PermissionType,
permLevel: PermissionLevel
) => {
// get the user's roles
const roleId = ctx.roleId || roles.BUILTIN_ROLE_IDS.PUBLIC
@ -122,7 +122,10 @@ export default (
// check general builder stuff, this middleware is a good way
// to find API endpoints which are builder focused
if (permType === permissions.PermissionType.BUILDER) {
if (
permType === permissions.PermissionType.BUILDER ||
permType === permissions.PermissionType.GLOBAL_BUILDER
) {
await builderMiddleware(ctx)
}

View File

@ -10,7 +10,7 @@ import {
setDebounce,
} from "../utilities/redis"
import { db as dbCore, cache } from "@budibase/backend-core"
import { UserCtx, Database, App } from "@budibase/types"
import { UserCtx, Database } from "@budibase/types"
const DEBOUNCE_TIME_SEC = 30

View File

@ -43,7 +43,6 @@ export interface User extends Document {
roles: UserRoles
builder?: {
global?: boolean
appBuilder?: boolean
apps?: string[]
}
admin?: {

View File

@ -18,3 +18,4 @@ export * from "./sso"
export * from "./user"
export * from "./cli"
export * from "./websocket"
export * from "./permissions"

View File

@ -0,0 +1,19 @@
export enum PermissionLevel {
READ = "read",
WRITE = "write",
EXECUTE = "execute",
ADMIN = "admin",
}
// these are the global types, that govern the underlying default behaviour
export enum PermissionType {
APP = "app",
TABLE = "table",
USER = "user",
AUTOMATION = "automation",
WEBHOOK = "webhook",
BUILDER = "builder",
GLOBAL_BUILDER = "globalBuilder",
VIEW = "view",
QUERY = "query",
}

View File

@ -433,26 +433,9 @@ export const inviteAccept = async (
}
}
export const grantAppBuilder = async (ctx: Ctx) => {
const { userId } = ctx.params
const user = await userSdk.db.getUser(userId)
if (!user.builder) {
user.builder = {}
}
user.builder.appBuilder = true
await userSdk.db.save(user, { hashPassword: false })
ctx.body = { message: `User "${user.email}" granted app builder permissions` }
}
export const addAppBuilder = async (ctx: Ctx) => {
const { userId, appId } = ctx.params
const user = await userSdk.db.getUser(userId)
if (!user.builder?.appBuilder && !userSdk.core.isGlobalBuilder(user)) {
ctx.throw(
400,
"Unable to update access, user must be granted app builder permissions."
)
}
if (userSdk.core.isGlobalBuilder(user)) {
ctx.body = { message: "User already admin - no permissions updated." }
return
@ -472,12 +455,6 @@ export const addAppBuilder = async (ctx: Ctx) => {
export const removeAppBuilder = async (ctx: Ctx) => {
const { userId, appId } = ctx.params
const user = await userSdk.db.getUser(userId)
if (!user.builder?.appBuilder && !userSdk.core.isGlobalBuilder(user)) {
ctx.throw(
400,
"Unable to update access, user must be granted app builder permissions."
)
}
if (userSdk.core.isGlobalBuilder(user)) {
ctx.body = { message: "User already admin - no permissions removed." }
return

View File

@ -24,27 +24,9 @@ describe("/api/global/users/:userId/app/builder", () => {
return response.body as User
}
async function grantAppBuilder(): Promise<User> {
const user = await newUser()
await config.api.users.grantAppBuilder(user._id!)
return await getUser(user._id!)
}
describe("POST /api/global/users/:userId/app/builder", () => {
it("should be able to grant a user builder permissions", async () => {
const user = await grantAppBuilder()
expect(user.builder?.appBuilder).toBe(true)
})
})
describe("PATCH /api/global/users/:userId/app/:appId/builder", () => {
it("shouldn't allow granting access to an app to a non-app builder", async () => {
const user = await newUser()
await config.api.users.grantBuilderToApp(user._id!, MOCK_APP_ID, 400)
})
it("should be able to grant a user access to a particular app", async () => {
const user = await grantAppBuilder()
const user = await newUser()
await config.api.users.grantBuilderToApp(user._id!, MOCK_APP_ID)
const updated = await getUser(user._id!)
expect(updated.builder?.appBuilder).toBe(true)
@ -54,7 +36,7 @@ describe("/api/global/users/:userId/app/builder", () => {
describe("DELETE /api/global/users/:userId/app/:appId/builder", () => {
it("should allow revoking access", async () => {
const user = await grantAppBuilder()
const user = await newUser()
await config.api.users.grantBuilderToApp(user._id!, MOCK_APP_ID)
let updated = await getUser(user._id!)
expect(updated.builder?.apps![0]).toBe(MOCK_APP_ID)

View File

@ -122,8 +122,7 @@ router
buildAdminInitValidation(),
controller.adminUser
)
.post("/api/global/users/:userId/app/builder", controller.grantAppBuilder)
.patch(
.post(
"/api/global/users/:userId/app/:appId/builder",
controller.addAppBuilder
)

View File

@ -141,30 +141,19 @@ export class UserAPI extends TestAPI {
.expect(opts?.status ? opts.status : 200)
}
grantAppBuilder = (userId: string) => {
return this.request
.post(`/api/global/users/${userId}/app/builder`)
.set(this.config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(200)
}
grantBuilderToApp = (
userId: string,
appId: string,
statusCode: number = 200
) => {
return this.request
.patch(`/api/global/users/${userId}/app/${appId}/builder`)
.post(`/api/global/users/${userId}/app/${appId}/builder`)
.set(this.config.defaultHeaders())
.expect("Content-Type", /json/)
.expect(statusCode)
}
revokeBuilderToApp = (
userId: string,
appId: string
) => {
revokeBuilderToApp = (userId: string, appId: string) => {
return this.request
.delete(`/api/global/users/${userId}/app/${appId}/builder`)
.set(this.config.defaultHeaders())