Skip to content

Commit

Permalink
Add users management page with tables per IDP.
Browse files Browse the repository at this point in the history
  • Loading branch information
srpiatt committed Mar 27, 2024
1 parent 02e1301 commit f8e1ef4
Show file tree
Hide file tree
Showing 17 changed files with 402 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/app.postcss
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ section.main-content.w-full {
}

h1 {
@apply h1 text-center my-1;
@apply h1 my-1;
}
h2 {
@apply h2 my-1;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/Content.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@

<section class={`main-content ${full && 'w-full'}`}>
{#if title}<h1 class="mb-4">{title}</h1>{/if}
{#if subtitle}<p class="subtitle mb-4 text-center">{subtitle}</p>{/if}
{#if subtitle}<p class="subtitle mb-4">{subtitle}</p>{/if}
<slot />
</section>
14 changes: 7 additions & 7 deletions src/lib/components/Navigation.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
const toastStore = getToastStore();
const routes = [
{ path: '/users', text: 'Users' },
{ path: '/user', text: 'User Management' },
{ path: '/explorer', text: 'Explorer' },
{ path: '/api', text: 'API' },
{ path: '/dataset', text: 'Dataset Management' },
Expand Down Expand Up @@ -62,13 +62,13 @@
async function loginUser(resp: string) {
await login(resp).catch((e) => {
console.log(e);
toastStore.trigger({
message:
'An error occured while validating your token. Please try again later. If this problem persists, please contact an administrator.',
background: 'variant-filled-error',
console.log(e);
toastStore.trigger({
message:
'An error occured while validating your token. Please try again later. If this problem persists, please contact an administrator.',
background: 'variant-filled-error',
});
});
});
}
function handleLogin() {
Expand Down
14 changes: 12 additions & 2 deletions src/lib/components/datatable/Table.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
// Parameters
export let search = false;
export let title = '';
export let defaultRowsPerPage = 5;
export let columns: Column[] = [];
export let cellOverides: Indexable = {};
Expand All @@ -31,9 +32,18 @@
</script>

<div class="overflow-x-auto space-y-4">
{#if search}
{#if title || search}
<header class="flex justify-between gap-4">
<Search {handler} />
{#if title}
<div class="flex-auto">
<h2>{title}</h2>
</div>
{/if}
{#if search}
<div class="flex-none">
<Search {handler} />
</div>
{/if}
</header>
{/if}
<table class="table table-hover table-compact w-full table-auto align-middle">
Expand Down
36 changes: 36 additions & 0 deletions src/lib/components/user/cell/Actions.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<script lang="ts">
export let data = { cell: '', row: { status: '' } };
</script>

<button
type="button"
title="Information"
class="bg-initial text-secondary-600 hover:text-primary-600"
>
<i class="fa-solid fa-circle-info fa-xl"></i>
<span class="sr-only">View Information</span>
</button>

{#if data.row.status === 'Active'}
<button type="button" title="Edit" class="bg-initial text-secondary-600 hover:text-primary-600">
<i class="fa-solid fa-pen-to-square fa-xl"></i>
<span class="sr-only">Edit</span>
</button>
<button
type="button"
title="Deactivate user"
class="bg-initial text-secondary-600 hover:text-primary-600"
>
<i class="fa-solid fa-circle-xmark fa-xl"></i>
<span class="sr-only">Deactivate user</span>
</button>
{:else}
<button
type="button"
title="Reactivate user"
class="bg-initial text-secondary-600 hover:text-primary-600"
>
<i class="fa-solid fa-circle-check fa-xl"></i>
<span class="sr-only">Reactivate user</span>
</button>
{/if}
7 changes: 7 additions & 0 deletions src/lib/components/user/cell/Status.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script lang="ts">
export let data = { cell: '', row: {} };
</script>

<span class={`p-1 rounded variant-filled-${data.cell === 'Active' ? 'success' : 'error'}`}
>{data.cell}</span
>
7 changes: 7 additions & 0 deletions src/lib/models/Connection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface Connection {
uuid: string;
id: string;
label: string;
subPrefix: string;
requiredFields: string;
}
14 changes: 14 additions & 0 deletions src/lib/models/Role.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface Privilege {
uuid: string;
name: string;
description: string;
queryScope: string;
application?: object;
}

export interface Role {
uuid: string;
name: string;
description: string;
privileges: Privilege[];
}
11 changes: 10 additions & 1 deletion src/lib/models/User.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import type { Role } from './Role';
import type { Connection } from './Connection';

export interface User {
uuid?: string;
email?: string;
privileges?: string[];
roles?: string[];
token?: string;
acceptedTOS?: boolean;
}

export interface ExtendedUser extends User {
subject?: string;
connection: Connection;
active: boolean;
roles: Role[];
}
2 changes: 1 addition & 1 deletion src/lib/stores/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const user: Writable<User> = writable({});

export async function getUser(force?: boolean) {
if (force || !get(user)?.privileges) {
const res = await api.get('psama/user/me?hasToken').then((res) => res);
const res = await api.get('psama/user/me?hasToken');
user.set(res);
}
}
Expand Down
33 changes: 33 additions & 0 deletions src/lib/stores/Users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { writable, type Writable } from 'svelte/store';

import { roles as mockRoles, users as mockUsers, connections as mockConns } from './mock/Users';
import type { ExtendedUser } from '../models/User';
import type { Connection } from '../models/Connection';
import type { Role } from '../models/Role';

// TODO: Break connections and roles out into different store files when working on super-admin page.
export const users: Writable<ExtendedUser[]> = writable([]);
export const roles: Writable<Role[]> = writable([]);
export const connections: Writable<Connection[]> = writable([]);

// TODO: Add api integration
export async function getUsers() {
users.set(mockUsers);
}

export async function getConnections() {
connections.set(mockConns);
}

export async function getRoles() {
roles.set(mockRoles);
}

export default {
users,
roles,
connections,
getUsers,
getRoles,
getConnections,
};
146 changes: 146 additions & 0 deletions src/lib/stores/mock/Users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// Mock api data to use until login flow is added. Excluded json data we could get but don't need -- excess fields
// recieved when setting response json object to data model will be ignored.
// TODO: Move these to the test mock-data folder when api has been inmplemented.

const _application = {
picsure: {
uuid: 'a1234',
name: 'PICSURE',
description: 'PIC-SURE multiple data access API',
enable: true,
},
jupyter: {
uuid: 'a2345',
name: 'JupyterHub',
description: 'JupyterHub authentication via PSAMA',
enable: true,
},
};

const _privileges = {
superAdmin: {
uuid: 'p1234',
name: 'SUPER_ADMIN',
description: 'PIC-SURE Auth super admin for managing roles/privileges/application/connections',
queryScope: '[]',
},
admin: {
uuid: 'p2345',
name: 'ADMIN',
description: 'PIC-SURE Auth admin for managing users.',
queryScope: '[]',
},
anyQuery: {
uuid: 'p3456',
name: 'PIC_SURE_ANY_QUERY',
description: 'User who cann run any PIC-SURE Query',
queryScope: '[]',
application: _application.picsure,
},
jupyter: {
uuid: 'p4567',
name: 'JUPYTER_USER',
description: 'JupyterHub user for accessing notebooks',
queryScope: '[]',
application: _application.jupyter,
},
};

const _roles = {
topAdmin: {
uuid: 'r1234',
name: 'PIC-SURE Top Admin',
description:
'PIC-SURE Auth Micro App Top admin including Admin and super Admin, can manage roles and privileges directly',
privileges: [_privileges.superAdmin, _privileges.admin],
},
user: {
uuid: 'r2345',
name: 'PIC-SURE User',
description: 'Normal user, can run any query including data export.',
privileges: [_privileges.anyQuery],
},
jupyterUser: {
uuid: 'r3456',
name: 'JupyterHub User',
description: 'The user is able to access JupyterHub as a normal user',
privileges: [_privileges.jupyter],
},
admin: {
uuid: 'r4567',
name: 'Admin',
description:
'Normal admin users, can manage other users including assignment of roles and privileges',
privileges: [_privileges.admin],
},
};
export const roles = Object.values(_roles);

const _connections = {
c1234: {
uuid: 'c1234',
label: 'Some IDP',
id: 'some-idp',
subPrefix: 'some-idp|',
requiredFields: '[{"label":"Email", "id":"email"}]',
},
c2345: {
uuid: 'c1234',
label: 'Another IDP',
id: 'another-idp',
subPrefix: 'another-idp|',
requiredFields: '[{"label":"Email", "id":"email"}]',
},
};
export const connections = Object.values(_connections);

export const users = [
{
uuid: 'abcd',
subject: _connections.c1234.subPrefix + 'abcd',
roles: [_roles.topAdmin, _roles.admin, _roles.user],
email: 'abcd@test.com',
connection: _connections.c1234,
active: true,
},
{
uuid: 'bcde',
subject: _connections.c1234.subPrefix + 'bcde',
roles: [_roles.topAdmin, _roles.admin, _roles.user],
email: 'bcde@test.com',
connection: _connections.c1234,
active: true,
},
{
uuid: 'cdef',
roles: [_roles.admin, _roles.user],
email: 'cdef@test.com',
connection: _connections.c1234,
generalMetadata: '{"email":"cdef@test.com"}',
active: false,
},
{
uuid: 'defg',
roles: [_roles.user],
email: 'defg@test.com',
connection: _connections.c2345,
generalMetadata: '{"email":"defg@test.com"}',
active: true,
},
{
uuid: 'efgh',
roles: [_roles.user],
email: 'efgh@test.com',
connection: _connections.c1234,
generalMetadata: '{"email":"efgh@test.com"}',
active: false,
},
{
uuid: 'fghi',
subject: 'google-oauth2|fghi',
roles: [_roles.user],
email: 'fghi@test.com',
connection: _connections.c1234,
active: true,
},
];
Loading

0 comments on commit f8e1ef4

Please sign in to comment.