Merge branch 'develop' into relationship-one-to-many
This commit is contained in:
commit
17e4ff1c60
|
@ -6,9 +6,11 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "0.7.6",
|
||||
"version": "0.7.7",
|
||||
"npmClient": "yarn",
|
||||
"packages": [
|
||||
"packages/*"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/builder",
|
||||
"version": "0.7.6",
|
||||
"version": "0.7.7",
|
||||
"license": "AGPL-3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
@ -64,9 +64,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^1.58.5",
|
||||
"@budibase/client": "^0.7.6",
|
||||
"@budibase/client": "^0.7.7",
|
||||
"@budibase/colorpicker": "1.0.1",
|
||||
"@budibase/string-templates": "^0.7.6",
|
||||
"@budibase/string-templates": "^0.7.7",
|
||||
"@budibase/svelte-ag-grid": "^0.0.16",
|
||||
"@sentry/browser": "5.19.1",
|
||||
"@svelteschool/svelte-forms": "0.7.0",
|
||||
|
|
|
@ -30,6 +30,7 @@ export const getBackendUiStore = () => {
|
|||
const queries = await queriesResponse.json()
|
||||
const integrationsResponse = await api.get("/api/integrations")
|
||||
const integrations = await integrationsResponse.json()
|
||||
const permissionLevels = await store.actions.permissions.fetchLevels()
|
||||
|
||||
store.update(state => {
|
||||
state.selectedDatabase = db
|
||||
|
@ -37,6 +38,7 @@ export const getBackendUiStore = () => {
|
|||
state.datasources = datasources
|
||||
state.queries = queries
|
||||
state.integrations = integrations
|
||||
state.permissionLevels = permissionLevels
|
||||
return state
|
||||
})
|
||||
},
|
||||
|
@ -333,6 +335,25 @@ export const getBackendUiStore = () => {
|
|||
return response
|
||||
},
|
||||
},
|
||||
permissions: {
|
||||
fetchLevels: async () => {
|
||||
const response = await api.get("/api/permission/levels")
|
||||
const json = await response.json()
|
||||
return json
|
||||
},
|
||||
forResource: async resourceId => {
|
||||
const response = await api.get(`/api/permission/${resourceId}`)
|
||||
const json = await response.json()
|
||||
return json
|
||||
},
|
||||
save: async ({ role, resource, level }) => {
|
||||
const response = await api.post(
|
||||
`/api/permission/${role}/${resource}/${level}`
|
||||
)
|
||||
const json = await response.json()
|
||||
return json
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return store
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import CreateViewButton from "./buttons/CreateViewButton.svelte"
|
||||
import ExportButton from "./buttons/ExportButton.svelte"
|
||||
import EditRolesButton from "./buttons/EditRolesButton.svelte"
|
||||
import ManageAccessButton from "./buttons/ManageAccessButton.svelte"
|
||||
import * as api from "./api"
|
||||
import Table from "./Table.svelte"
|
||||
import { TableNames } from "constants"
|
||||
|
@ -47,6 +48,7 @@
|
|||
title={isUsersTable ? 'Create New User' : 'Create New Row'}
|
||||
modalContentComponent={isUsersTable ? CreateEditUser : CreateEditRow} />
|
||||
<CreateViewButton />
|
||||
<ManageAccessButton resourceId={$backendUiStore.selectedTable?._id} />
|
||||
<ExportButton view={tableView} />
|
||||
{/if}
|
||||
{#if isUsersTable}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
import GroupByButton from "./buttons/GroupByButton.svelte"
|
||||
import FilterButton from "./buttons/FilterButton.svelte"
|
||||
import ExportButton from "./buttons/ExportButton.svelte"
|
||||
import ManageAccessButton from "./buttons/ManageAccessButton.svelte"
|
||||
|
||||
export let view = {}
|
||||
|
||||
|
@ -53,5 +54,6 @@
|
|||
{#if view.calculation}
|
||||
<GroupByButton {view} />
|
||||
{/if}
|
||||
<ManageAccessButton resourceId={decodeURI(name)} />
|
||||
<ExportButton {view} />
|
||||
</Table>
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<script>
|
||||
import { TextButton, Icon, Popover } from "@budibase/bbui"
|
||||
import { backendUiStore } from "builderStore"
|
||||
import { Roles } from "constants/backend"
|
||||
import api from "builderStore/api"
|
||||
import ManageAccessPopover from "../popovers/ManageAccessPopover.svelte"
|
||||
|
||||
export let resourceId
|
||||
|
||||
let anchor
|
||||
let dropdown
|
||||
let levels
|
||||
let permissions
|
||||
|
||||
async function openDropdown() {
|
||||
permissions = await backendUiStore.actions.permissions.forResource(
|
||||
resourceId
|
||||
)
|
||||
levels = await backendUiStore.actions.permissions.fetchLevels()
|
||||
dropdown.show()
|
||||
}
|
||||
</script>
|
||||
|
||||
<div bind:this={anchor}>
|
||||
<TextButton text small on:click={openDropdown}>
|
||||
<i class="ri-lock-line" />
|
||||
Manage Access
|
||||
</TextButton>
|
||||
</div>
|
||||
<Popover bind:this={dropdown} {anchor} align="left">
|
||||
<ManageAccessPopover
|
||||
{resourceId}
|
||||
{levels}
|
||||
{permissions}
|
||||
onClosed={dropdown.hide} />
|
||||
</Popover>
|
||||
|
||||
<style>
|
||||
i {
|
||||
margin-right: var(--spacing-xs);
|
||||
font-size: var(--font-size-s);
|
||||
}
|
||||
</style>
|
|
@ -145,7 +145,7 @@
|
|||
thin
|
||||
text="Use as table display column" />
|
||||
|
||||
<Label gray small>Search Indexes</Label>
|
||||
<Label grey small>Search Indexes</Label>
|
||||
<Toggle
|
||||
checked={indexes[0] === field.name}
|
||||
disabled={indexes[1] === field.name}
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
<script>
|
||||
import { onMount } from "svelte"
|
||||
import { backendUiStore } from "builderStore"
|
||||
import { Roles } from "constants/backend"
|
||||
import api from "builderStore/api"
|
||||
import { notifier } from "builderStore/store/notifications"
|
||||
import { Button, Label, Input, Select, Spacer } from "@budibase/bbui"
|
||||
|
||||
export let resourceId
|
||||
export let permissions
|
||||
export let onClosed
|
||||
|
||||
async function changePermission(level, role) {
|
||||
await backendUiStore.actions.permissions.save({
|
||||
level,
|
||||
role,
|
||||
resource: resourceId,
|
||||
})
|
||||
|
||||
// Show updated permissions in UI: REMOVE
|
||||
permissions = await backendUiStore.actions.permissions.forResource(
|
||||
resourceId
|
||||
)
|
||||
notifier.success("Updated permissions.")
|
||||
// TODO: update permissions
|
||||
// permissions[]
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="popover">
|
||||
<h5>Who Can Access This Data?</h5>
|
||||
<div class="note">
|
||||
<Label extraSmall grey>Specify the minimum access level role for this data.</Label>
|
||||
</div>
|
||||
<Spacer large />
|
||||
<div class="row">
|
||||
<Label extraSmall grey>Level</Label>
|
||||
<Label extraSmall grey>Role</Label>
|
||||
{#each Object.keys(permissions) as level}
|
||||
<Input secondary thin value={level} disabled={true} />
|
||||
<Select
|
||||
secondary
|
||||
thin
|
||||
value={permissions[level]}
|
||||
on:change={e => changePermission(level, e.target.value)}>
|
||||
{#each $backendUiStore.roles as role}
|
||||
<option value={role._id}>{role.name}</option>
|
||||
{/each}
|
||||
</Select>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<Spacer large />
|
||||
|
||||
<div class="footer">
|
||||
<Button secondary on:click={onClosed}>Cancel</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.popover {
|
||||
display: grid;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: var(--spacing-s) 0 var(--spacing-m) 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: var(--spacing-m);
|
||||
margin-top: var(--spacing-l);
|
||||
}
|
||||
|
||||
.row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: var(--spacing-m);
|
||||
}
|
||||
|
||||
.note {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
|
@ -92,3 +92,11 @@ export const HostingTypes = {
|
|||
CLOUD: "cloud",
|
||||
SELF: "self",
|
||||
}
|
||||
|
||||
export const Roles = {
|
||||
ADMIN: "ADMIN",
|
||||
POWER: "POWER",
|
||||
BASIC: "BASIC",
|
||||
PUBLIC: "PUBLIC",
|
||||
BUILDER: "BUILDER",
|
||||
}
|
||||
|
|
|
@ -48,6 +48,11 @@
|
|||
modal.show()
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
template = null
|
||||
modal.hide()
|
||||
}
|
||||
|
||||
checkIfKeysAndApps()
|
||||
</script>
|
||||
|
||||
|
@ -73,7 +78,7 @@
|
|||
<AppList />
|
||||
</div>
|
||||
|
||||
<Modal bind:this={modal} padding={false} width="600px">
|
||||
<Modal bind:this={modal} padding={false} width="600px" on:hide={closeModal}>
|
||||
<CreateAppModal {hasKey} {template} />
|
||||
</Modal>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/client",
|
||||
"version": "0.7.6",
|
||||
"version": "0.7.7",
|
||||
"license": "MPL-2.0",
|
||||
"main": "dist/budibase-client.js",
|
||||
"module": "dist/budibase-client.js",
|
||||
|
@ -9,14 +9,14 @@
|
|||
"dev:builder": "rollup -cw"
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/string-templates": "^0.7.6",
|
||||
"@budibase/string-templates": "^0.7.7",
|
||||
"deep-equal": "^2.0.1",
|
||||
"regexparam": "^1.3.0",
|
||||
"shortid": "^2.2.15",
|
||||
"svelte-spa-router": "^3.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@budibase/standard-components": "^0.7.6",
|
||||
"@budibase/standard-components": "^0.7.7",
|
||||
"@rollup/plugin-commonjs": "^16.0.0",
|
||||
"@rollup/plugin-node-resolve": "^10.0.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/server",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "0.7.6",
|
||||
"version": "0.7.7",
|
||||
"description": "Budibase Web Server",
|
||||
"main": "src/electron.js",
|
||||
"repository": {
|
||||
|
@ -50,8 +50,8 @@
|
|||
"author": "Budibase",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@budibase/client": "^0.7.6",
|
||||
"@budibase/string-templates": "^0.7.6",
|
||||
"@budibase/client": "^0.7.7",
|
||||
"@budibase/string-templates": "^0.7.7",
|
||||
"@elastic/elasticsearch": "7.10.0",
|
||||
"@koa/router": "8.0.0",
|
||||
"@sendgrid/mail": "7.1.1",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const {
|
||||
BUILTIN_PERMISSIONS,
|
||||
PermissionLevels,
|
||||
isPermissionLevelHigherThanRead,
|
||||
higherPermission,
|
||||
} = require("../../utilities/security/permissions")
|
||||
const {
|
||||
|
@ -12,12 +13,34 @@ const {
|
|||
const { getRoleParams } = require("../../db/utils")
|
||||
const CouchDB = require("../../db")
|
||||
const { cloneDeep } = require("lodash/fp")
|
||||
const {
|
||||
CURRENTLY_SUPPORTED_LEVELS,
|
||||
getBasePermissions,
|
||||
} = require("../../utilities/security/utilities")
|
||||
|
||||
const PermissionUpdateType = {
|
||||
REMOVE: "remove",
|
||||
ADD: "add",
|
||||
}
|
||||
|
||||
const SUPPORTED_LEVELS = CURRENTLY_SUPPORTED_LEVELS
|
||||
|
||||
// quick function to perform a bit of weird logic, make sure fetch calls
|
||||
// always say a write role also has read permission
|
||||
function fetchLevelPerms(permissions, level, roleId) {
|
||||
if (!permissions) {
|
||||
permissions = {}
|
||||
}
|
||||
permissions[level] = roleId
|
||||
if (
|
||||
isPermissionLevelHigherThanRead(level) &&
|
||||
!permissions[PermissionLevels.READ]
|
||||
) {
|
||||
permissions[PermissionLevels.READ] = roleId
|
||||
}
|
||||
return permissions
|
||||
}
|
||||
|
||||
// utility function to stop this repetition - permissions always stored under roles
|
||||
async function getAllDBRoles(db) {
|
||||
const body = await db.allDocs(
|
||||
|
@ -65,7 +88,10 @@ async function updatePermissionOnRole(
|
|||
}
|
||||
// handle the adding, we're on the correct role, at it to this
|
||||
if (!remove && role._id === dbRoleId) {
|
||||
rolePermissions[resourceId] = level
|
||||
rolePermissions[resourceId] = higherPermission(
|
||||
rolePermissions[resourceId],
|
||||
level
|
||||
)
|
||||
updated = true
|
||||
}
|
||||
// handle the update, add it to bulk docs to perform at end
|
||||
|
@ -89,7 +115,7 @@ exports.fetchBuiltin = function(ctx) {
|
|||
|
||||
exports.fetchLevels = function(ctx) {
|
||||
// for now only provide the read/write perms externally
|
||||
ctx.body = [PermissionLevels.WRITE, PermissionLevels.READ]
|
||||
ctx.body = SUPPORTED_LEVELS
|
||||
}
|
||||
|
||||
exports.fetch = async function(ctx) {
|
||||
|
@ -98,20 +124,25 @@ exports.fetch = async function(ctx) {
|
|||
let permissions = {}
|
||||
// create an object with structure role ID -> resource ID -> level
|
||||
for (let role of roles) {
|
||||
if (role.permissions) {
|
||||
const roleId = getExternalRoleID(role._id)
|
||||
if (permissions[roleId] == null) {
|
||||
permissions[roleId] = {}
|
||||
}
|
||||
for (let [resource, level] of Object.entries(role.permissions)) {
|
||||
permissions[roleId][resource] = higherPermission(
|
||||
permissions[roleId][resource],
|
||||
level
|
||||
)
|
||||
}
|
||||
if (!role.permissions) {
|
||||
continue
|
||||
}
|
||||
const roleId = getExternalRoleID(role._id)
|
||||
for (let [resource, level] of Object.entries(role.permissions)) {
|
||||
permissions[resource] = fetchLevelPerms(
|
||||
permissions[resource],
|
||||
level,
|
||||
roleId
|
||||
)
|
||||
}
|
||||
}
|
||||
ctx.body = permissions
|
||||
// apply the base permissions
|
||||
const finalPermissions = {}
|
||||
for (let [resource, permission] of Object.entries(permissions)) {
|
||||
const basePerms = getBasePermissions(resource)
|
||||
finalPermissions[resource] = Object.assign(basePerms, permission)
|
||||
}
|
||||
ctx.body = finalPermissions
|
||||
}
|
||||
|
||||
exports.getResourcePerms = async function(ctx) {
|
||||
|
@ -123,18 +154,20 @@ exports.getResourcePerms = async function(ctx) {
|
|||
})
|
||||
)
|
||||
const roles = body.rows.map(row => row.doc)
|
||||
const resourcePerms = {}
|
||||
for (let role of roles) {
|
||||
let permissions = {}
|
||||
for (let level of SUPPORTED_LEVELS) {
|
||||
// update the various roleIds in the resource permissions
|
||||
if (role.permissions && role.permissions[resourceId]) {
|
||||
const roleId = getExternalRoleID(role._id)
|
||||
resourcePerms[roleId] = higherPermission(
|
||||
resourcePerms[roleId],
|
||||
role.permissions[resourceId]
|
||||
)
|
||||
for (let role of roles) {
|
||||
if (role.permissions && role.permissions[resourceId] === level) {
|
||||
permissions = fetchLevelPerms(
|
||||
permissions,
|
||||
level,
|
||||
getExternalRoleID(role._id)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.body = resourcePerms
|
||||
ctx.body = Object.assign(getBasePermissions(resourceId), permissions)
|
||||
}
|
||||
|
||||
exports.addPermission = async function(ctx) {
|
||||
|
|
|
@ -57,7 +57,7 @@ exports.fetch = async function(ctx) {
|
|||
include_docs: true,
|
||||
})
|
||||
)
|
||||
const roles = body.rows.map(row => row.doc)
|
||||
let roles = body.rows.map(row => row.doc)
|
||||
|
||||
// need to combine builtin with any DB record of them (for sake of permissions)
|
||||
for (let builtinRoleId of EXTERNAL_BUILTIN_ROLE_IDS) {
|
||||
|
@ -68,6 +68,8 @@ exports.fetch = async function(ctx) {
|
|||
if (dbBuiltin == null) {
|
||||
roles.push(builtinRole)
|
||||
} else {
|
||||
// remove role and all back after combining with the builtin
|
||||
roles = roles.filter(role => role._id !== dbBuiltin._id)
|
||||
dbBuiltin._id = getExternalRoleID(dbBuiltin._id)
|
||||
roles.push(Object.assign(builtinRole, dbBuiltin))
|
||||
}
|
||||
|
|
|
@ -71,21 +71,22 @@ describe("/permission", () => {
|
|||
.set(defaultHeaders(appId))
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
expect(res.body[STD_ROLE_ID]).toEqual("read")
|
||||
expect(res.body["read"]).toEqual(STD_ROLE_ID)
|
||||
expect(res.body["write"]).toEqual(HIGHER_ROLE_ID)
|
||||
})
|
||||
|
||||
it("should get resource permissions with multiple roles", async () => {
|
||||
perms = await addPermission(request, appId, HIGHER_ROLE_ID, table._id, "write")
|
||||
const res = await getTablePermissions()
|
||||
expect(res.body[HIGHER_ROLE_ID]).toEqual("write")
|
||||
expect(res.body[STD_ROLE_ID]).toEqual("read")
|
||||
expect(res.body["read"]).toEqual(STD_ROLE_ID)
|
||||
expect(res.body["write"]).toEqual(HIGHER_ROLE_ID)
|
||||
const allRes = await request
|
||||
.get(`/api/permission`)
|
||||
.set(defaultHeaders(appId))
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
expect(allRes.body[HIGHER_ROLE_ID][table._id]).toEqual("write")
|
||||
expect(allRes.body[STD_ROLE_ID][table._id]).toEqual("read")
|
||||
expect(allRes.body[table._id]["write"]).toEqual(HIGHER_ROLE_ID)
|
||||
expect(allRes.body[table._id]["read"]).toEqual(STD_ROLE_ID)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ const Router = require("@koa/router")
|
|||
const viewController = require("../controllers/view")
|
||||
const rowController = require("../controllers/row")
|
||||
const authorized = require("../../middleware/authorized")
|
||||
const { paramResource } = require("../../middleware/resourceId")
|
||||
const {
|
||||
BUILDER,
|
||||
PermissionTypes,
|
||||
|
@ -15,12 +16,14 @@ router
|
|||
.get("/api/views/export", authorized(BUILDER), viewController.exportView)
|
||||
.get(
|
||||
"/api/views/:viewName",
|
||||
paramResource("viewName"),
|
||||
authorized(PermissionTypes.VIEW, PermissionLevels.READ),
|
||||
rowController.fetchView
|
||||
)
|
||||
.get("/api/views", authorized(BUILDER), viewController.fetch)
|
||||
.delete(
|
||||
"/api/views/:viewName",
|
||||
paramResource("viewName"),
|
||||
authorized(BUILDER),
|
||||
usage,
|
||||
viewController.destroy
|
||||
|
|
|
@ -23,6 +23,22 @@ function Permission(type, level) {
|
|||
this.type = type
|
||||
}
|
||||
|
||||
function levelToNumber(perm) {
|
||||
switch (perm) {
|
||||
// not everything has execute privileges
|
||||
case PermissionLevels.EXECUTE:
|
||||
return 0
|
||||
case PermissionLevels.READ:
|
||||
return 1
|
||||
case PermissionLevels.WRITE:
|
||||
return 2
|
||||
case PermissionLevels.ADMIN:
|
||||
return 3
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the specified permission level for the user return the levels they are allowed to carry out.
|
||||
* @param {string} userPermLevel The permission level of the user.
|
||||
|
@ -47,6 +63,7 @@ function getAllowedLevels(userPermLevel) {
|
|||
}
|
||||
|
||||
exports.BUILTIN_PERMISSION_IDS = {
|
||||
PUBLIC: "public",
|
||||
READ_ONLY: "read_only",
|
||||
WRITE: "write",
|
||||
ADMIN: "admin",
|
||||
|
@ -54,6 +71,13 @@ exports.BUILTIN_PERMISSION_IDS = {
|
|||
}
|
||||
|
||||
exports.BUILTIN_PERMISSIONS = {
|
||||
PUBLIC: {
|
||||
_id: exports.BUILTIN_PERMISSION_IDS.PUBLIC,
|
||||
name: "Public",
|
||||
permissions: [
|
||||
new Permission(PermissionTypes.WEBHOOK, PermissionLevels.EXECUTE),
|
||||
],
|
||||
},
|
||||
READ_ONLY: {
|
||||
_id: exports.BUILTIN_PERMISSION_IDS.READ_ONLY,
|
||||
name: "Read only",
|
||||
|
@ -97,6 +121,11 @@ exports.BUILTIN_PERMISSIONS = {
|
|||
},
|
||||
}
|
||||
|
||||
exports.getBuiltinPermissionByID = id => {
|
||||
const perms = Object.values(exports.BUILTIN_PERMISSIONS)
|
||||
return perms.find(perm => perm._id === id)
|
||||
}
|
||||
|
||||
exports.doesHaveResourcePermission = (
|
||||
permissions,
|
||||
permLevel,
|
||||
|
@ -144,22 +173,11 @@ exports.doesHaveBasePermission = (permType, permLevel, permissionIds) => {
|
|||
}
|
||||
|
||||
exports.higherPermission = (perm1, perm2) => {
|
||||
function toNum(perm) {
|
||||
switch (perm) {
|
||||
// not everything has execute privileges
|
||||
case PermissionLevels.EXECUTE:
|
||||
return 0
|
||||
case PermissionLevels.READ:
|
||||
return 1
|
||||
case PermissionLevels.WRITE:
|
||||
return 2
|
||||
case PermissionLevels.ADMIN:
|
||||
return 3
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return toNum(perm1) > toNum(perm2) ? perm1 : perm2
|
||||
return levelToNumber(perm1) > levelToNumber(perm2) ? perm1 : perm2
|
||||
}
|
||||
|
||||
exports.isPermissionLevelHigherThanRead = level => {
|
||||
return levelToNumber(level) > 1
|
||||
}
|
||||
|
||||
// utility as a lot of things need simply the builder permission
|
||||
|
|
|
@ -37,7 +37,7 @@ exports.BUILTIN_ROLES = {
|
|||
.addPermission(BUILTIN_PERMISSION_IDS.WRITE)
|
||||
.addInheritance(BUILTIN_IDS.PUBLIC),
|
||||
PUBLIC: new Role(BUILTIN_IDS.PUBLIC, "Public").addPermission(
|
||||
BUILTIN_PERMISSION_IDS.READ_ONLY
|
||||
BUILTIN_PERMISSION_IDS.PUBLIC
|
||||
),
|
||||
BUILDER: new Role(BUILTIN_IDS.BUILDER, "Builder").addPermission(
|
||||
BUILTIN_PERMISSION_IDS.ADMIN
|
||||
|
@ -56,6 +56,41 @@ function isBuiltin(role) {
|
|||
return exports.BUILTIN_ROLE_ID_ARRAY.some(builtin => role.includes(builtin))
|
||||
}
|
||||
|
||||
/**
|
||||
* Works through the inheritance ranks to see how far up the builtin stack this ID is.
|
||||
*/
|
||||
function builtinRoleToNumber(id) {
|
||||
const MAX = Object.values(BUILTIN_IDS).length + 1
|
||||
if (id === BUILTIN_IDS.ADMIN || id === BUILTIN_IDS.BUILDER) {
|
||||
return MAX
|
||||
}
|
||||
let role = exports.BUILTIN_ROLES[id],
|
||||
count = 0
|
||||
do {
|
||||
if (!role) {
|
||||
break
|
||||
}
|
||||
role = exports.BUILTIN_ROLES[role.inherits]
|
||||
count++
|
||||
} while (role !== null)
|
||||
return count
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whichever builtin roleID is lower.
|
||||
*/
|
||||
exports.lowerBuiltinRoleID = (roleId1, roleId2) => {
|
||||
if (!roleId1) {
|
||||
return roleId2
|
||||
}
|
||||
if (!roleId2) {
|
||||
return roleId1
|
||||
}
|
||||
return builtinRoleToNumber(roleId1) > builtinRoleToNumber(roleId2)
|
||||
? roleId2
|
||||
: roleId1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the role object, this is mainly useful for two purposes, to check if the level exists and
|
||||
* to check if the role inherits any others.
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
const {
|
||||
PermissionLevels,
|
||||
PermissionTypes,
|
||||
getBuiltinPermissionByID,
|
||||
isPermissionLevelHigherThanRead,
|
||||
} = require("../../utilities/security/permissions")
|
||||
const {
|
||||
lowerBuiltinRoleID,
|
||||
BUILTIN_ROLES,
|
||||
} = require("../../utilities/security/roles")
|
||||
const { DocumentTypes } = require("../../db/utils")
|
||||
|
||||
const CURRENTLY_SUPPORTED_LEVELS = [
|
||||
PermissionLevels.WRITE,
|
||||
PermissionLevels.READ,
|
||||
]
|
||||
|
||||
exports.getPermissionType = resourceId => {
|
||||
const docType = Object.values(DocumentTypes).filter(docType =>
|
||||
resourceId.startsWith(docType)
|
||||
)[0]
|
||||
switch (docType) {
|
||||
case DocumentTypes.TABLE:
|
||||
case DocumentTypes.ROW:
|
||||
return PermissionTypes.TABLE
|
||||
case DocumentTypes.AUTOMATION:
|
||||
return PermissionTypes.AUTOMATION
|
||||
case DocumentTypes.WEBHOOK:
|
||||
return PermissionTypes.WEBHOOK
|
||||
case DocumentTypes.QUERY:
|
||||
case DocumentTypes.DATASOURCE:
|
||||
return PermissionTypes.QUERY
|
||||
default:
|
||||
// views don't have an ID, will end up here
|
||||
return PermissionTypes.VIEW
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* works out the basic permissions based on builtin roles for a resource, using its ID
|
||||
* @param resourceId
|
||||
* @returns {{}}
|
||||
*/
|
||||
exports.getBasePermissions = resourceId => {
|
||||
const type = exports.getPermissionType(resourceId)
|
||||
const permissions = {}
|
||||
for (let [roleId, role] of Object.entries(BUILTIN_ROLES)) {
|
||||
if (!role.permissionId) {
|
||||
continue
|
||||
}
|
||||
const perms = getBuiltinPermissionByID(role.permissionId)
|
||||
const typedPermission = perms.permissions.find(perm => perm.type === type)
|
||||
if (
|
||||
typedPermission &&
|
||||
CURRENTLY_SUPPORTED_LEVELS.indexOf(typedPermission.level) !== -1
|
||||
) {
|
||||
const level = typedPermission.level
|
||||
permissions[level] = lowerBuiltinRoleID(permissions[level], roleId)
|
||||
if (isPermissionLevelHigherThanRead(level)) {
|
||||
permissions[PermissionLevels.READ] = lowerBuiltinRoleID(
|
||||
permissions[PermissionLevels.READ],
|
||||
roleId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return permissions
|
||||
}
|
||||
|
||||
exports.CURRENTLY_SUPPORTED_LEVELS = CURRENTLY_SUPPORTED_LEVELS
|
|
@ -35,7 +35,7 @@
|
|||
"keywords": [
|
||||
"svelte"
|
||||
],
|
||||
"version": "0.7.6",
|
||||
"version": "0.7.7",
|
||||
"license": "MIT",
|
||||
"gitHead": "1a80b09fd093f2599a68f7db72ad639dd50922dd",
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/string-templates",
|
||||
"version": "0.7.6",
|
||||
"version": "0.7.7",
|
||||
"description": "Handlebars wrapper for Budibase templating.",
|
||||
"main": "src/index.js",
|
||||
"module": "src/index.js",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/deployment",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "0.7.6",
|
||||
"version": "0.7.7",
|
||||
"description": "Budibase Deployment Server",
|
||||
"main": "src/index.js",
|
||||
"repository": {
|
||||
|
|
Loading…
Reference in New Issue