RBAC popover complete
This commit is contained in:
parent
63e0e187a6
commit
20bf34d53c
|
@ -30,6 +30,7 @@ export const getBackendUiStore = () => {
|
||||||
const queries = await queriesResponse.json()
|
const queries = await queriesResponse.json()
|
||||||
const integrationsResponse = await api.get("/api/integrations")
|
const integrationsResponse = await api.get("/api/integrations")
|
||||||
const integrations = await integrationsResponse.json()
|
const integrations = await integrationsResponse.json()
|
||||||
|
const permissionLevels = await store.actions.permissions.fetchLevels()
|
||||||
|
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.selectedDatabase = db
|
state.selectedDatabase = db
|
||||||
|
@ -37,6 +38,7 @@ export const getBackendUiStore = () => {
|
||||||
state.datasources = datasources
|
state.datasources = datasources
|
||||||
state.queries = queries
|
state.queries = queries
|
||||||
state.integrations = integrations
|
state.integrations = integrations
|
||||||
|
state.permissionLevels = permissionLevels
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -351,6 +353,13 @@ export const getBackendUiStore = () => {
|
||||||
const json = await response.json()
|
const json = await response.json()
|
||||||
return json
|
return json
|
||||||
},
|
},
|
||||||
|
delete: async ({ role, resource, level }) => {
|
||||||
|
const response = await api.delete(
|
||||||
|
`/api/permission/${role}/${resource}/${level}`
|
||||||
|
)
|
||||||
|
const json = await response.json()
|
||||||
|
return json
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,8 +48,8 @@
|
||||||
title={isUsersTable ? 'Create New User' : 'Create New Row'}
|
title={isUsersTable ? 'Create New User' : 'Create New Row'}
|
||||||
modalContentComponent={isUsersTable ? CreateEditUser : CreateEditRow} />
|
modalContentComponent={isUsersTable ? CreateEditUser : CreateEditRow} />
|
||||||
<CreateViewButton />
|
<CreateViewButton />
|
||||||
<ExportButton view={tableView} />
|
|
||||||
<ManageAccessButton resourceId={$backendUiStore.selectedTable?._id} />
|
<ManageAccessButton resourceId={$backendUiStore.selectedTable?._id} />
|
||||||
|
<ExportButton view={tableView} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if isUsersTable}
|
{#if isUsersTable}
|
||||||
<EditRolesButton />
|
<EditRolesButton />
|
||||||
|
|
|
@ -54,6 +54,6 @@
|
||||||
{#if view.calculation}
|
{#if view.calculation}
|
||||||
<GroupByButton {view} />
|
<GroupByButton {view} />
|
||||||
{/if}
|
{/if}
|
||||||
<ExportButton {view} />
|
|
||||||
<ManageAccessButton resourceId={decodeURI(name)} />
|
<ManageAccessButton resourceId={decodeURI(name)} />
|
||||||
|
<ExportButton {view} />
|
||||||
</Table>
|
</Table>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { TextButton, Icon, Popover } from "@budibase/bbui"
|
import { TextButton, Icon, Popover } from "@budibase/bbui"
|
||||||
|
import { backendUiStore } from "builderStore"
|
||||||
|
import { Roles } from "constants/backend"
|
||||||
import api from "builderStore/api"
|
import api from "builderStore/api"
|
||||||
import ManageAccessPopover from "../popovers/ManageAccessPopover.svelte"
|
import ManageAccessPopover from "../popovers/ManageAccessPopover.svelte"
|
||||||
|
|
||||||
|
@ -7,16 +9,30 @@
|
||||||
|
|
||||||
let anchor
|
let anchor
|
||||||
let dropdown
|
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>
|
</script>
|
||||||
|
|
||||||
<div bind:this={anchor}>
|
<div bind:this={anchor}>
|
||||||
<TextButton text small on:click={dropdown.show}>
|
<TextButton text small on:click={openDropdown}>
|
||||||
<i class="ri-lock-line" />
|
<i class="ri-lock-line" />
|
||||||
Manage Access
|
Manage Access
|
||||||
</TextButton>
|
</TextButton>
|
||||||
</div>
|
</div>
|
||||||
<Popover bind:this={dropdown} {anchor} align="left">
|
<Popover bind:this={dropdown} {anchor} align="left">
|
||||||
<ManageAccessPopover {resourceId} onClosed={dropdown.hide} />
|
<ManageAccessPopover
|
||||||
|
{resourceId}
|
||||||
|
{levels}
|
||||||
|
{permissions}
|
||||||
|
onClosed={dropdown.hide} />
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -142,7 +142,7 @@
|
||||||
thin
|
thin
|
||||||
text="Use as table display column" />
|
text="Use as table display column" />
|
||||||
|
|
||||||
<Label gray small>Search Indexes</Label>
|
<Label grey small>Search Indexes</Label>
|
||||||
<Toggle
|
<Toggle
|
||||||
checked={indexes[0] === field.name}
|
checked={indexes[0] === field.name}
|
||||||
disabled={indexes[1] === field.name}
|
disabled={indexes[1] === field.name}
|
||||||
|
|
|
@ -1,73 +1,94 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
|
import { Roles } from "constants/backend"
|
||||||
import api from "builderStore/api"
|
import api from "builderStore/api"
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import { Button, Select } from "@budibase/bbui"
|
import { Button, Label, Select, Spacer } from "@budibase/bbui"
|
||||||
|
|
||||||
const FORMATS = [
|
|
||||||
{
|
|
||||||
name: "CSV",
|
|
||||||
key: "csv",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "JSON",
|
|
||||||
key: "json",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export let resourceId
|
export let resourceId
|
||||||
|
export let permissions
|
||||||
|
export let levels
|
||||||
export let onClosed
|
export let onClosed
|
||||||
|
|
||||||
let permissions = {}
|
// Draft level and role for editing
|
||||||
let levels = []
|
let level = levels[0]
|
||||||
|
let role = Roles.BASIC
|
||||||
|
|
||||||
async function exportView() {
|
$: permissionKeys = Object.keys(permissions)
|
||||||
onClosed()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function changePermission(level, role) {
|
async function addPermission() {
|
||||||
console.log({ role, resourceId, level })
|
|
||||||
await backendUiStore.actions.permissions.save({
|
await backendUiStore.actions.permissions.save({
|
||||||
|
level,
|
||||||
role,
|
role,
|
||||||
resource: resourceId,
|
resource: resourceId,
|
||||||
level,
|
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
onMount(async () => {
|
// Show updated permissions in UI
|
||||||
// TODO: possibly cleaner
|
|
||||||
permissions = await backendUiStore.actions.permissions.forResource(
|
permissions = await backendUiStore.actions.permissions.forResource(
|
||||||
resourceId
|
resourceId
|
||||||
)
|
)
|
||||||
levels = await backendUiStore.actions.permissions.fetchLevels()
|
notifier.success("Access rule saved.")
|
||||||
})
|
|
||||||
|
// Reset the draft permissions
|
||||||
|
level = levels[0]
|
||||||
|
role = Roles.BASIC
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deletePermission(level, role) {
|
||||||
|
await backendUiStore.actions.permissions.delete({ level, role, resourceId })
|
||||||
|
delete permissions[role]
|
||||||
|
notifier.danger("Removed access rule.")
|
||||||
|
permissions = permissions
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="popover">
|
<div class="popover">
|
||||||
<h5>Manage Access</h5>
|
<h5>Who Can Access This Data?</h5>
|
||||||
{#each levels as level}
|
<Spacer large />
|
||||||
<Select
|
<div class="row">
|
||||||
label={level}
|
<Label extraSmall grey>Level</Label>
|
||||||
secondary
|
<Label extraSmall grey>Role</Label>
|
||||||
thin
|
<div />
|
||||||
value={permissions[level]}
|
{#if permissionKeys.length === 0}
|
||||||
on:change={e => changePermission(level, e.target.value)}>
|
<Label extraSmall>Default Access Rules Applied.</Label>
|
||||||
|
{/if}
|
||||||
|
{#each permissionKeys as role}
|
||||||
|
<Label small>{permissions[role]}</Label>
|
||||||
|
<Label small>{role}</Label>
|
||||||
|
<i
|
||||||
|
class="ri-close-circle-line delete"
|
||||||
|
on:click={() => deletePermission(permissions[role], role)} />
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Spacer large />
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<Label small>Add Rule</Label>
|
||||||
|
<Spacer small />
|
||||||
|
<div class="draft-permission">
|
||||||
|
<Select label="Level" secondary thin bind:value={level}>
|
||||||
|
{#each levels as level}
|
||||||
|
<option value={level}>{level}</option>
|
||||||
|
{/each}
|
||||||
|
</Select>
|
||||||
|
<Select label="Role" secondary thin bind:value={role}>
|
||||||
{#each $backendUiStore.roles as role}
|
{#each $backendUiStore.roles as role}
|
||||||
<option value={role._id}>{role.name}</option>
|
<option value={role._id}>{role.name}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</Select>
|
</Select>
|
||||||
{/each}
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<Button secondary on:click={onClosed}>Cancel</Button>
|
<Button secondary on:click={onClosed}>Cancel</Button>
|
||||||
<!-- <Button primary on:click={}>Save</Button> -->
|
<Button primary on:click={addPermission}>Edit Rules</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.popover {
|
.popover {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-gap: var(--spacing-xl);
|
width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h5 {
|
h5 {
|
||||||
|
@ -75,9 +96,30 @@
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: var(--spacing-s) 0 var(--spacing-m) 0;
|
||||||
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
gap: var(--spacing-m);
|
gap: var(--spacing-m);
|
||||||
|
margin-top: var(--spacing-l);
|
||||||
|
}
|
||||||
|
|
||||||
|
.draft-permission {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 20px;
|
||||||
|
grid-gap: var(--spacing-s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete {
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -92,3 +92,11 @@ export const HostingTypes = {
|
||||||
CLOUD: "cloud",
|
CLOUD: "cloud",
|
||||||
SELF: "self",
|
SELF: "self",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const Roles = {
|
||||||
|
ADMIN: "ADMIN",
|
||||||
|
POWER: "POWER",
|
||||||
|
BASIC: "BASIC",
|
||||||
|
PUBLIC: "PUBLIC",
|
||||||
|
BUILDER: "BUILDER",
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue