Updates to remove app builder concept, denying access to app creation for app builders.
This commit is contained in:
parent
c277b065db
commit
64a5426d36
|
@ -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
|
||||
|
|
|
@ -15,7 +15,7 @@ router
|
|||
)
|
||||
.post(
|
||||
"/api/applications",
|
||||
authorized(permissions.BUILDER),
|
||||
authorized(permissions.GLOBAL_BUILDER),
|
||||
applicationValidator(),
|
||||
controller.create
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -43,7 +43,6 @@ export interface User extends Document {
|
|||
roles: UserRoles
|
||||
builder?: {
|
||||
global?: boolean
|
||||
appBuilder?: boolean
|
||||
apps?: string[]
|
||||
}
|
||||
admin?: {
|
||||
|
|
|
@ -18,3 +18,4 @@ export * from "./sso"
|
|||
export * from "./user"
|
||||
export * from "./cli"
|
||||
export * from "./websocket"
|
||||
export * from "./permissions"
|
||||
|
|
|
@ -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",
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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())
|
||||
|
|
Loading…
Reference in New Issue