Remove licensing restriction for configuring access on views
This commit is contained in:
parent
608bc97cdd
commit
eb20686de8
|
@ -45,18 +45,6 @@ async function updatePermissionOnRole(
|
|||
}: { roleId: string; resourceId: string; level: PermissionLevel },
|
||||
updateType: PermissionUpdateType
|
||||
) {
|
||||
const allowedAction = await sdk.permissions.resourceActionAllowed({
|
||||
resourceId,
|
||||
level,
|
||||
})
|
||||
|
||||
if (!allowedAction.allowed) {
|
||||
throw new HTTPError(
|
||||
`You are not allowed to '${allowedAction.level}' the resource type '${allowedAction.resourceType}'`,
|
||||
403
|
||||
)
|
||||
}
|
||||
|
||||
const db = context.getAppDB()
|
||||
const remove = updateType === PermissionUpdateType.REMOVE
|
||||
const isABuiltin = roles.isBuiltin(roleId)
|
||||
|
@ -182,9 +170,6 @@ export async function getResourcePerms(
|
|||
},
|
||||
{} as Record<string, ResourcePermissionInfo>
|
||||
),
|
||||
requiresPlanToModify: (
|
||||
await sdk.permissions.allowsExplicitPermissions(resourceId)
|
||||
).minPlan,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
const mockedSdk = sdk.permissions as jest.Mocked<typeof sdk.permissions>
|
||||
jest.mock("../../../sdk/app/permissions", () => ({
|
||||
...jest.requireActual("../../../sdk/app/permissions"),
|
||||
resourceActionAllowed: jest.fn(),
|
||||
}))
|
||||
|
||||
import sdk from "../../../sdk"
|
||||
|
||||
|
@ -40,8 +36,6 @@ describe("/permission", () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
mocks.licenses.useCloudFree()
|
||||
mockedSdk.resourceActionAllowed.mockResolvedValue({ allowed: true })
|
||||
|
||||
table = (await config.createTable()) as typeof table
|
||||
row = await config.createRow()
|
||||
view = await config.api.viewV2.create({
|
||||
|
@ -112,29 +106,6 @@ describe("/permission", () => {
|
|||
expect(allRes.body[table._id]["read"]).toEqual(STD_ROLE_ID)
|
||||
expect(allRes.body[table._id]["write"]).toEqual(HIGHER_ROLE_ID)
|
||||
})
|
||||
|
||||
it("throw forbidden if the action is not allowed for the resource", async () => {
|
||||
mockedSdk.resourceActionAllowed.mockResolvedValue({
|
||||
allowed: false,
|
||||
resourceType: DocumentType.DATASOURCE,
|
||||
level: PermissionLevel.READ,
|
||||
})
|
||||
|
||||
await config.api.permission.add(
|
||||
{
|
||||
roleId: STD_ROLE_ID,
|
||||
resourceId: table._id,
|
||||
level: PermissionLevel.EXECUTE,
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
body: {
|
||||
message:
|
||||
"You are not allowed to 'read' the resource type 'datasource'",
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("remove", () => {
|
||||
|
@ -148,29 +119,6 @@ describe("/permission", () => {
|
|||
const permsRes = await config.api.permission.get(table._id)
|
||||
expect(permsRes.permissions[STD_ROLE_ID]).toBeUndefined()
|
||||
})
|
||||
|
||||
it("throw forbidden if the action is not allowed for the resource", async () => {
|
||||
mockedSdk.resourceActionAllowed.mockResolvedValue({
|
||||
allowed: false,
|
||||
resourceType: DocumentType.DATASOURCE,
|
||||
level: PermissionLevel.READ,
|
||||
})
|
||||
|
||||
await config.api.permission.revoke(
|
||||
{
|
||||
roleId: STD_ROLE_ID,
|
||||
resourceId: table._id,
|
||||
level: PermissionLevel.EXECUTE,
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
body: {
|
||||
message:
|
||||
"You are not allowed to 'read' the resource type 'datasource'",
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("check public user allowed", () => {
|
||||
|
|
|
@ -1,151 +0,0 @@
|
|||
import { db, roles } from "@budibase/backend-core"
|
||||
import { features } from "@budibase/pro"
|
||||
import {
|
||||
DocumentType,
|
||||
PermissionLevel,
|
||||
PermissionSource,
|
||||
PlanType,
|
||||
VirtualDocumentType,
|
||||
} from "@budibase/types"
|
||||
import { extractViewInfoFromID, isViewID } from "../../../db/utils"
|
||||
import {
|
||||
CURRENTLY_SUPPORTED_LEVELS,
|
||||
getBasePermissions,
|
||||
} from "../../../utilities/security"
|
||||
import sdk from "../../../sdk"
|
||||
import { isV2 } from "../views"
|
||||
|
||||
type ResourceActionAllowedResult =
|
||||
| { allowed: true }
|
||||
| {
|
||||
allowed: false
|
||||
level: PermissionLevel
|
||||
resourceType: DocumentType | VirtualDocumentType
|
||||
}
|
||||
|
||||
export async function resourceActionAllowed({
|
||||
resourceId,
|
||||
level,
|
||||
}: {
|
||||
resourceId: string
|
||||
level: PermissionLevel
|
||||
}): Promise<ResourceActionAllowedResult> {
|
||||
if (!isViewID(resourceId)) {
|
||||
return { allowed: true }
|
||||
}
|
||||
|
||||
if (await features.isViewPermissionEnabled()) {
|
||||
return { allowed: true }
|
||||
}
|
||||
|
||||
return {
|
||||
allowed: false,
|
||||
level,
|
||||
resourceType: VirtualDocumentType.VIEW,
|
||||
}
|
||||
}
|
||||
|
||||
type ResourcePermissions = Record<
|
||||
string,
|
||||
{ role: string; type: PermissionSource }
|
||||
>
|
||||
|
||||
export async function getInheritablePermissions(
|
||||
resourceId: string
|
||||
): Promise<ResourcePermissions | undefined> {
|
||||
if (isViewID(resourceId)) {
|
||||
return await getResourcePerms(extractViewInfoFromID(resourceId).tableId)
|
||||
}
|
||||
}
|
||||
|
||||
export async function allowsExplicitPermissions(resourceId: string) {
|
||||
if (isViewID(resourceId)) {
|
||||
const allowed = await features.isViewPermissionEnabled()
|
||||
const minPlan = !allowed ? PlanType.PREMIUM_PLUS : undefined
|
||||
|
||||
return {
|
||||
allowed,
|
||||
minPlan,
|
||||
}
|
||||
}
|
||||
|
||||
return { allowed: true }
|
||||
}
|
||||
|
||||
export async function getResourcePerms(
|
||||
resourceId: string
|
||||
): Promise<ResourcePermissions> {
|
||||
const rolesList = await roles.getAllRoles()
|
||||
|
||||
let permissions: ResourcePermissions = {}
|
||||
|
||||
const permsToInherit = await getInheritablePermissions(resourceId)
|
||||
|
||||
const allowsExplicitPerm = (await allowsExplicitPermissions(resourceId))
|
||||
.allowed
|
||||
|
||||
for (let level of CURRENTLY_SUPPORTED_LEVELS) {
|
||||
// update the various roleIds in the resource permissions
|
||||
for (let role of rolesList) {
|
||||
const rolePerms = allowsExplicitPerm
|
||||
? roles.checkForRoleResourceArray(role.permissions || {}, resourceId)
|
||||
: {}
|
||||
if (rolePerms[resourceId]?.indexOf(level) > -1) {
|
||||
permissions[level] = {
|
||||
role: roles.getExternalRoleID(role._id!, role.version),
|
||||
type: PermissionSource.EXPLICIT,
|
||||
}
|
||||
} else if (
|
||||
!permissions[level] &&
|
||||
permsToInherit &&
|
||||
permsToInherit[level]
|
||||
) {
|
||||
permissions[level] = {
|
||||
role: permsToInherit[level].role,
|
||||
type: PermissionSource.INHERITED,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const basePermissions = Object.entries(
|
||||
getBasePermissions(resourceId)
|
||||
).reduce<ResourcePermissions>((p, [level, role]) => {
|
||||
p[level] = { role, type: PermissionSource.BASE }
|
||||
return p
|
||||
}, {})
|
||||
const result = Object.assign(basePermissions, permissions)
|
||||
return result
|
||||
}
|
||||
|
||||
export async function getDependantResources(
|
||||
resourceId: string
|
||||
): Promise<Record<string, number> | undefined> {
|
||||
if (db.isTableId(resourceId)) {
|
||||
const dependants: Record<string, Set<string>> = {}
|
||||
|
||||
const table = await sdk.tables.getTable(resourceId)
|
||||
const views = Object.values(table.views || {})
|
||||
|
||||
for (const view of views) {
|
||||
if (!isV2(view)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const permissions = await getResourcePerms(view.id)
|
||||
for (const [, roleInfo] of Object.entries(permissions)) {
|
||||
if (roleInfo.type === PermissionSource.INHERITED) {
|
||||
dependants[VirtualDocumentType.VIEW] ??= new Set()
|
||||
dependants[VirtualDocumentType.VIEW].add(view.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Object.entries(dependants).reduce((p, [type, resources]) => {
|
||||
p[type] = resources.size
|
||||
return p
|
||||
}, {} as Record<string, number>)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
import { PermissionLevel } from "@budibase/types"
|
||||
import { mocks, structures } from "@budibase/backend-core/tests"
|
||||
import { resourceActionAllowed } from ".."
|
||||
import { generateViewID } from "../../../../db/utils"
|
||||
import { initProMocks } from "../../../../tests/utilities/mocks/pro"
|
||||
|
||||
initProMocks()
|
||||
|
||||
describe("permissions sdk", () => {
|
||||
beforeEach(() => {
|
||||
mocks.licenses.useCloudFree()
|
||||
})
|
||||
|
||||
describe("resourceActionAllowed", () => {
|
||||
it("non view resources actions are always allowed", async () => {
|
||||
const resourceId = structures.users.user()._id!
|
||||
|
||||
const result = await resourceActionAllowed({
|
||||
resourceId,
|
||||
level: PermissionLevel.READ,
|
||||
})
|
||||
|
||||
expect(result).toEqual({ allowed: true })
|
||||
})
|
||||
|
||||
it("view resources actions allowed if the feature flag is enabled", async () => {
|
||||
mocks.licenses.useViewPermissions()
|
||||
const resourceId = generateViewID(structures.generator.guid())
|
||||
|
||||
const result = await resourceActionAllowed({
|
||||
resourceId,
|
||||
level: PermissionLevel.READ,
|
||||
})
|
||||
|
||||
expect(result).toEqual({ allowed: true })
|
||||
})
|
||||
|
||||
it("view resources actions allowed if the feature flag is disabled", async () => {
|
||||
const resourceId = generateViewID(structures.generator.guid())
|
||||
|
||||
const result = await resourceActionAllowed({
|
||||
resourceId,
|
||||
level: PermissionLevel.READ,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
allowed: false,
|
||||
level: "read",
|
||||
resourceType: "view",
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -8,7 +8,6 @@ export interface ResourcePermissionInfo {
|
|||
|
||||
export interface GetResourcePermsResponse {
|
||||
permissions: Record<string, ResourcePermissionInfo>
|
||||
requiresPlanToModify?: PlanType
|
||||
}
|
||||
|
||||
export interface GetDependantResourcesResponse {
|
||||
|
|
Loading…
Reference in New Issue