Skip to content

Commit

Permalink
Add roles authorization page and subpages.
Browse files Browse the repository at this point in the history
  • Loading branch information
srpiatt committed Apr 18, 2024
1 parent f0a60c8 commit dd23433
Show file tree
Hide file tree
Showing 24 changed files with 987 additions and 23 deletions.
54 changes: 54 additions & 0 deletions src/lib/components/admin/authorization/PrivilegeForm.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script lang="ts">
import type { Privilege } from '$lib/models/Privileges';
export let privilege: Privilege | undefined = undefined;
export let applicationList: string[][];
export let onSave = () => {};
let name;
let description;
let application;
</script>

<label class="label">
<span>Name:</span>
<input type="text" bind:this={name} value={privilege ? privilege.name : ''} class="input" />
</label>

<label class="label">
<span>Description:</span>
<input
type="text"
bind:this={description}
value={privilege ? privilege.description : ''}
class="input"
/>
</label>

<label class="label">
<span>Application:</span>
<select class="select" bind:this={application}>
<option selected={!privilege || !privilege.application} value>none</option>
{#each applicationList as [name, uuid]}
<option value={uuid} selected={privilege && privilege.application === uuid}>{name}</option>
{/each}
</select>
</label>

<button
type="submit"
class="btn variant-ghost-primary hover:variant-filled-primary"
aria-label="You are on the save button"
on:click={onSave}
>
Save
</button>
<a href="/admin/authorization" class="btn variant-ghost-secondary hover:variant-filled-secondary">
Cancel
</a>

<style>
label {
margin: 0.5em 0;
}
</style>
59 changes: 59 additions & 0 deletions src/lib/components/admin/authorization/RoleForm.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<script lang="ts">
import type { Role } from '$lib/models/Role';
export let role: Role | undefined = undefined;
export let privilegeList: string[][];
export let onSave = () => {};
let name;
let description;
let privileges: HTMLElement[] = [];
</script>

<label class="label">
<span>Name:</span>
<input type="text" bind:this={name} value={role ? role.name : ''} class="input" />
</label>

<label class="label">
<span>Description:</span>
<input type="text" bind:this={description} value={role ? role.description : ''} class="input" />
</label>

<fieldset data-testid="privilege-checkboxes">
<legend>Privileges:</legend>
{#each privilegeList as [name, uuid], index}
<label class="flex items-center space-x-2">
<input
class="checkbox"
type="checkbox"
bind:this={privileges[index]}
value={uuid}
checked={role && role.privileges.includes(uuid)}
/>
<p>{name}</p>
</label>
{/each}
</fieldset>

<button
type="submit"
class="btn variant-ghost-primary hover:variant-filled-primary"
aria-label="You are on the save button"
on:click={onSave}
>
Save
</button>
<a href="/admin/authorization" class="btn variant-ghost-secondary hover:variant-filled-secondary">
Cancel
</a>

<style>
label,
fieldset {
margin: 0.5em 0;
}
fieldset label {
margin: 0;
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script lang="ts">
import ApplicationStore from '$lib/stores/Application';
const { getApplication } = ApplicationStore;
export let data: { cell: '' };
</script>

{data.cell ? getApplication(data.cell).name : 'none'}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<script lang="ts">
import { getModalStore } from '@skeletonlabs/skeleton';
const modalStore = getModalStore();
export let data = { cell: '', row: { name: '' } };
function deleteModal() {
modalStore.trigger({
type: 'confirm',
title: 'Delete Privilege?',
body: `Are you sure you want to delete privilege '${data.row.name}'?`,
buttonTextConfirm: 'Yes',
buttonTextCancel: 'No',
response: (confirm: boolean) => {
if (!confirm) return;
// TODO: add delete logic using data.cell
},
});
}
</script>

<a
data-testid={`privilege-view-btn-${data.cell}`}
href={`/admin/authorization/privilege/${data.cell}`}
class="text-secondary-600 hover:text-primary-600"
>
<i class="fa-solid fa-circle-info fa-xl"></i>
<span class="sr-only">View Privilege</span>
</a>
<a
data-testid={`privilege-edit-btn-${data.cell}`}
href={`/admin/authorization/privilege/${data.cell}/edit`}
class="text-secondary-600 hover:text-primary-600"
>
<i class="fa-solid fa-pen-to-square fa-xl"></i>
<span class="sr-only">Edit</span>
</a>
<button
data-testid={`privilege-delete-btn-${data.cell}`}
type="button"
title="Delete"
class="bg-initial text-secondary-600 hover:text-primary-600"
on:click={deleteModal}
>
<i class="fa-solid fa-trash fa-xl"></i>
<span class="sr-only">Delete</span>
</button>
47 changes: 47 additions & 0 deletions src/lib/components/admin/authorization/cell/RoleActions.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<script lang="ts">
import { getModalStore } from '@skeletonlabs/skeleton';
const modalStore = getModalStore();
export let data = { cell: '', row: { name: '' } };
function deleteModal() {
modalStore.trigger({
type: 'confirm',
title: 'Delete Role?',
body: `Are you sure you want to delete role '${data.row.name}'?`,
buttonTextConfirm: 'Yes',
buttonTextCancel: 'No',
response: (confirm: boolean) => {
if (!confirm) return;
// TODO: add delete logic using data.cell
},
});
}
</script>

<a
data-testid={`role-view-btn-${data.cell}`}
href={`/admin/authorization/role/${data.cell}`}
class="text-secondary-600 hover:text-primary-600"
>
<i class="fa-solid fa-circle-info fa-xl"></i>
<span class="sr-only">View Role</span>
</a>
<a
data-testid={`role-edit-btn-${data.cell}`}
href={`/admin/authorization/role/${data.cell}/edit`}
class="text-secondary-600 hover:text-primary-600"
>
<i class="fa-solid fa-pen-to-square fa-xl"></i>
<span class="sr-only">Edit</span>
</a>
<button
data-testid={`role-delete-btn-${data.cell}`}
type="button"
title="Delete"
class="bg-initial text-secondary-600 hover:text-primary-600"
on:click={deleteModal}
>
<i class="fa-solid fa-trash fa-xl"></i>
<span class="sr-only">Delete</span>
</button>
8 changes: 8 additions & 0 deletions src/lib/models/Applications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface Application {
uuid: string;
name: string;
description: string;
token: string;
url: string;
enable: boolean;
}
17 changes: 17 additions & 0 deletions src/lib/models/Privileges.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export interface Privilege {
uuid: string;
name: string;
description: string;
queryScope: string;
application: string;
}

// TODO: Replace any type
/* eslint-disable @typescript-eslint/no-explicit-any */
export function mapPrivilege(data: any) {
return {
...data,
application: data.application?.uuid || '',
};
}
/* eslint-enable @typescript-eslint/no-explicit-any */
18 changes: 10 additions & 8 deletions src/lib/models/Role.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
export interface Privilege {
export interface Role {
uuid: string;
name: string;
description: string;
queryScope: string;
application?: object;
privileges: string[];
}

export interface Role {
uuid: string;
name: string;
description: string;
privileges: Privilege[];
// TODO: Replace any type
/* eslint-disable @typescript-eslint/no-explicit-any */
export function mapRole(data: any) {
return {
...data,
privileges: data.privileges.map((p: any) => p.uuid) || [],
};
}
/* eslint-enable @typescript-eslint/no-explicit-any */
12 changes: 11 additions & 1 deletion src/lib/models/User.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Role } from './Role';
import { mapRole, type Role } from './Role';
import type { Connection } from './Connection';

export interface User {
Expand All @@ -15,3 +15,13 @@ export interface ExtendedUser extends User {
active: boolean;
roles: Role[];
}

// TODO: Replace metadata nad query types
/* eslint-disable @typescript-eslint/no-explicit-any */
export function mapUser(data: any) {
return {
...data,
roles: data.roles.map(mapRole),
};
}
/* eslint-enable @typescript-eslint/no-explicit-any */
30 changes: 30 additions & 0 deletions src/lib/stores/Application.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { get, derived, writable, type Writable } from 'svelte/store';
import { type Application } from '$lib/models/Applications';

import { applications as mockApps } from './mock/data';

/* eslint-disable @typescript-eslint/no-unused-vars */
const APP_URL = 'psama/application';
/* eslint-enable @typescript-eslint/no-unused-vars */

const applications: Writable<Application[]> = writable([]);
const applicationList = derived(applications, ($a) => $a.map((a) => [a.name, a.uuid]));

// TODO: Add api integration
async function loadApplications() {
applications.set(mockApps);
}

function getApplication(uuid: string) {
const store: Application[] = get(applications);
const appIndex: number = store.findIndex((a) => a.uuid === uuid);
return store[appIndex];
}

export default {
subscribe: applications.subscribe,
applications,
applicationList,
loadApplications,
getApplication,
};
30 changes: 30 additions & 0 deletions src/lib/stores/Privileges.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { get, derived, writable, type Writable } from 'svelte/store';
import { type Privilege, mapPrivilege } from '$lib/models/Privileges';

import { privileges as mockPrivileges } from './mock/data';

/* eslint-disable @typescript-eslint/no-unused-vars */
const PRIV_PATH = 'psama/privilege';
/* eslint-enable @typescript-eslint/no-unused-vars */

const privileges: Writable<Privilege[]> = writable([]);
const privilegeList = derived(privileges, ($p) => $p.map((p) => [p.name, p.uuid]));

// TODO: Add api integration
async function loadPrivileges() {
privileges.set(mockPrivileges.map(mapPrivilege));
}

async function getPrivilege(uuid: string) {
const store: Privilege[] = get(privileges);
const privIndex: number = store.findIndex((p) => p.uuid === uuid);
return store[privIndex];
}

export default {
subscribe: privileges.subscribe,
privileges,
privilegeList,
loadPrivileges,
getPrivilege,
};
28 changes: 28 additions & 0 deletions src/lib/stores/Roles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { get, writable, type Writable } from 'svelte/store';
import { type Role, mapRole } from '$lib/models/Role';

import { roles as mockRoles } from './mock/data';

/* eslint-disable @typescript-eslint/no-unused-vars */
const ROLE_PATH = '/psama/role';
/* eslint-enable @typescript-eslint/no-unused-vars */

const roles: Writable<Role[]> = writable([]);

// TODO: Add api integration
async function loadRoles() {
roles.set(mockRoles.map(mapRole));
}

async function getRole(uuid: string) {
const store: Role[] = get(roles);
const roleIndex: number = store.findIndex((r) => r.uuid === uuid);
return store[roleIndex];
}

export default {
subscribe: roles.subscribe,
roles,
loadRoles,
getRole,
};
Loading

0 comments on commit dd23433

Please sign in to comment.