RBAC popover complete

This commit is contained in:
Martin McKeaveney 2021-02-10 22:23:27 +00:00
parent 63e0e187a6
commit 20bf34d53c
7 changed files with 116 additions and 41 deletions

View File

@ -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
},
}, },
} }

View File

@ -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 />

View File

@ -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>

View File

@ -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>

View File

@ -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}

View File

@ -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>

View File

@ -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",
}