Guard permission
This commit is contained in:
parent
623b385d8a
commit
00119f9d73
|
@ -742,5 +742,37 @@ describe("/rowsActions", () => {
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const { PUBLIC, ...nonPublicRoles } = roles.BUILTIN_ROLE_IDS
|
||||||
|
|
||||||
|
it.each(Object.values(nonPublicRoles))(
|
||||||
|
"rejects if the user does not have table read permission",
|
||||||
|
async role => {
|
||||||
|
await config.api.permission.add({
|
||||||
|
level: PermissionLevel.READ,
|
||||||
|
resourceId: tableId,
|
||||||
|
roleId: role,
|
||||||
|
})
|
||||||
|
|
||||||
|
const normalUser = await config.createUser({
|
||||||
|
admin: { global: false },
|
||||||
|
builder: {},
|
||||||
|
})
|
||||||
|
await config.withUser(normalUser, async () => {
|
||||||
|
await config.publish()
|
||||||
|
await config.api.rowAction.trigger(
|
||||||
|
tableId,
|
||||||
|
rowAction.id,
|
||||||
|
{
|
||||||
|
rowId: row._id!,
|
||||||
|
},
|
||||||
|
{ status: 403, body: { message: "User does not have permission" } }
|
||||||
|
)
|
||||||
|
|
||||||
|
const automationLogs = await getAutomationLogs()
|
||||||
|
expect(automationLogs).toBeEmpty()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -88,7 +88,7 @@ const authorized =
|
||||||
opts = { schema: false },
|
opts = { schema: false },
|
||||||
resourcePath?: string
|
resourcePath?: string
|
||||||
) =>
|
) =>
|
||||||
async (ctx: any, next: any) => {
|
async (ctx: UserCtx, next: any) => {
|
||||||
// webhooks don't need authentication, each webhook unique
|
// webhooks don't need authentication, each webhook unique
|
||||||
// also internal requests (between services) don't need authorized
|
// also internal requests (between services) don't need authorized
|
||||||
if (isWebhookEndpoint(ctx) || ctx.internal) {
|
if (isWebhookEndpoint(ctx) || ctx.internal) {
|
||||||
|
|
|
@ -1,44 +1,64 @@
|
||||||
import { Next } from "koa"
|
import { Next } from "koa"
|
||||||
import { UserCtx } from "@budibase/types"
|
import { PermissionLevel, PermissionType, UserCtx } from "@budibase/types"
|
||||||
import { paramSubResource } from "./resourceId"
|
import { paramSubResource } from "./resourceId"
|
||||||
import { docIds } from "@budibase/backend-core"
|
import { docIds } from "@budibase/backend-core"
|
||||||
import * as utils from "../db/utils"
|
import * as utils from "../db/utils"
|
||||||
import sdk from "../sdk"
|
import sdk from "../sdk"
|
||||||
|
import { authorizedResource } from "./authorized"
|
||||||
async function executeMiddleware(
|
|
||||||
ctx: UserCtx,
|
|
||||||
delegate: (ctx: UserCtx, next: any) => any
|
|
||||||
) {
|
|
||||||
await new Promise<void>(r => delegate(ctx, () => r()))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function triggerRowActionAuthorised(
|
export function triggerRowActionAuthorised(
|
||||||
sourcePath: string,
|
sourcePath: string,
|
||||||
actionPath: string
|
actionPath: string
|
||||||
) {
|
) {
|
||||||
async function extractResourceIds(ctx: UserCtx) {
|
return async (ctx: UserCtx, next: Next) => {
|
||||||
ctx = { ...ctx }
|
async function getResourceIds() {
|
||||||
// Reusing the existing middleware to extract the value
|
// Reusing the existing middleware to extract the value
|
||||||
await executeMiddleware(ctx, paramSubResource(sourcePath, actionPath))
|
await paramSubResource(sourcePath, actionPath)(ctx, () => {})
|
||||||
|
|
||||||
const sourceId: string = ctx.resourceId
|
const sourceId: string = ctx.resourceId
|
||||||
const rowActionId: String = ctx.subResourceId
|
const rowActionId: string = ctx.subResourceId
|
||||||
|
|
||||||
const isTableId = docIds.isTableId(sourceId)
|
const isTableId = docIds.isTableId(sourceId)
|
||||||
const isViewId = utils.isViewID(sourceId)
|
const isViewId = utils.isViewID(sourceId)
|
||||||
if (!isTableId && !isViewId) {
|
if (!isTableId && !isViewId) {
|
||||||
ctx.throw(400, `'${sourceId}' is not a valid source id`)
|
ctx.throw(400, `'${sourceId}' is not a valid source id`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const tableId = isTableId
|
||||||
|
? sourceId
|
||||||
|
: utils.extractViewInfoFromID(sourceId).tableId
|
||||||
|
const viewId = isTableId ? undefined : sourceId
|
||||||
|
return { tableId, viewId, rowActionId }
|
||||||
}
|
}
|
||||||
|
|
||||||
const tableId = isTableId
|
async function guardResourcePermissions(
|
||||||
? sourceId
|
ctx: UserCtx,
|
||||||
: utils.extractViewInfoFromID(sourceId).tableId
|
tableId: string,
|
||||||
const viewId = isTableId ? undefined : sourceId
|
viewId?: string
|
||||||
return { tableId, viewId, rowActionId }
|
) {
|
||||||
}
|
const { params } = ctx
|
||||||
|
try {
|
||||||
|
if (!viewId) {
|
||||||
|
ctx.params = { tableId }
|
||||||
|
await authorizedResource(
|
||||||
|
PermissionType.TABLE,
|
||||||
|
PermissionLevel.READ,
|
||||||
|
"tableId"
|
||||||
|
)(ctx, () => {})
|
||||||
|
} else {
|
||||||
|
ctx.params = { viewId }
|
||||||
|
await authorizedResource(
|
||||||
|
PermissionType.VIEW,
|
||||||
|
PermissionLevel.READ,
|
||||||
|
"__viewId"
|
||||||
|
)(ctx, () => {})
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
ctx.params = params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return async (ctx: UserCtx, next: Next) => {
|
const { tableId, viewId, rowActionId } = await getResourceIds()
|
||||||
const { tableId, viewId, rowActionId } = extractResourceIds(ctx)
|
|
||||||
|
|
||||||
const rowAction = await sdk.rowActions.get(tableId, rowActionId)
|
const rowAction = await sdk.rowActions.get(tableId, rowActionId)
|
||||||
|
|
||||||
|
@ -54,6 +74,8 @@ export function triggerRowActionAuthorised(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await guardResourcePermissions(ctx, tableId, viewId)
|
||||||
|
|
||||||
// Enrich tableId
|
// Enrich tableId
|
||||||
ctx.params.tableId = tableId
|
ctx.params.tableId = tableId
|
||||||
return next()
|
return next()
|
||||||
|
|
Loading…
Reference in New Issue