From b3be0440d120d7c7dd3ef9346be5f54e2ba990a6 Mon Sep 17 00:00:00 2001 From: Markus Thielker Date: Sun, 6 Apr 2025 13:02:56 +0200 Subject: [PATCH] NORY-59: refactor identity queries to use server actions --- dashboard/src/app/(inside)/user/[id]/page.tsx | 149 +++++++++--------- dashboard/src/lib/action/identity.ts | 46 ++++++ dashboard/src/lib/permission.ts | 1 + 3 files changed, 123 insertions(+), 73 deletions(-) diff --git a/dashboard/src/app/(inside)/user/[id]/page.tsx b/dashboard/src/app/(inside)/user/[id]/page.tsx index 98a578a..e590abc 100644 --- a/dashboard/src/app/(inside)/user/[id]/page.tsx +++ b/dashboard/src/app/(inside)/user/[id]/page.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { getIdentityApi } from '@/ory/sdk/server'; import { ErrorDisplay } from '@/components/error'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { IdentityTraits } from '@/components/identity/identity-traits'; @@ -14,6 +13,8 @@ import { IdentityCredentials } from '@/components/identity/identity-credentials' import { checkPermission, requirePermission, requireSession } from '@/lib/action/authentication'; import { permission, relation } from '@/lib/permission'; import { redirect } from 'next/navigation'; +import InsufficientPermission from '@/components/insufficient-permission'; +import { getIdentity, getIdentitySchema, listIdentitySessions } from '@/lib/action/identity'; interface MergedAddress { recovery_id?: string; @@ -91,65 +92,65 @@ export default async function UserDetailsPage({ params }: { params: Promise<{ id const pmEditUser = await checkPermission(permission.user.it, relation.edit, identityId); const pmDeleteUser = await checkPermission(permission.user.it, relation.delete, identityId); + const pmAccessUserTraits = await checkPermission(permission.user.trait, relation.access, identityId); const pmEditUserState = await checkPermission(permission.user.state, relation.edit, identityId); + const pmAccessUserSession = await checkPermission(permission.user.session, relation.access, identityId); const pmDeleteUserSession = await checkPermission(permission.user.session, relation.delete, identityId); const pmCreateUserCode = await checkPermission(permission.user.code, relation.create, identityId); const pmCreateUserLink = await checkPermission(permission.user.link, relation.create, identityId); const detailIdentityId = (await params).id; + const detailIdentity = pmAccessUser && await getIdentity(detailIdentityId); - const identityApi = await getIdentityApi(); - const identity = await identityApi.getIdentity({ id: detailIdentityId }) - .then((response) => { - return response.data; - }) - .catch(() => { - console.log('Identity not found'); - }); - - const sessions = await identityApi.listIdentitySessions({ id: detailIdentityId }) - .then((response) => response.data) - .catch(() => { - console.log('No sessions found'); - }); - - if (!identity) { + if (!detailIdentity) { return ; } - if (!identity.verifiable_addresses || !identity.verifiable_addresses[0]) { + if (!detailIdentity.verifiable_addresses || !detailIdentity.verifiable_addresses[0]) { return ; } - const identitySchema = await identityApi - .getIdentitySchema({ id: identity.schema_id }) - .then((response) => response.data as KratosSchema); + + const detailIdentitySessions = pmAccessUserSession && await listIdentitySessions(detailIdentityId); + + const detailIdentitySchema = await getIdentitySchema(detailIdentity.schema_id) + .then((response) => response as KratosSchema); const addresses = mergeAddresses( - identity.recovery_addresses ?? [], - identity.verifiable_addresses ?? [], + detailIdentity.recovery_addresses ?? [], + detailIdentity.verifiable_addresses ?? [], ); return (

{addresses[0].value}

-

{identity.id}

+

{detailIdentity.id}

- - - Traits - All identity properties specified in the identity schema - - - - - + { + pmAccessUserTraits ? + + + Traits + All identity properties specified in the identity + schema + + + + + + : + + } Actions @@ -157,7 +158,7 @@ export default async function UserDetailsPage({ params }: { params: Promise<{ id All authentication mechanisms registered with this identity - + @@ -229,46 +230,48 @@ export default async function UserDetailsPage({ params }: { params: Promise<{ id See and manage all sessions of this identity - - - - OS - Browser - Active since - - - - { - sessions ? - sessions.map((session) => { + { + detailIdentitySessions ? +
+ + + OS + Browser + Active since + + + + { + detailIdentitySessions.map((session) => { - const device = session.devices![0]; - const parser = new UAParser(device.user_agent); - const result = parser.getResult(); + const device = session.devices![0]; + const parser = new UAParser(device.user_agent); + const result = parser.getResult(); - return ( - - - {result.os.name} - {result.os.version} - - - {result.browser.name} - {result.browser.version} - - - {new Date(session.authenticated_at!).toLocaleString()} - - - ); - }) - : - - } - -
+ return ( + + + {result.os.name} + {result.os.version} + + + {result.browser.name} + {result.browser.version} + + + {new Date(session.authenticated_at!).toLocaleString()} + + + ); + }) + } + + + : +

This user has no active sessions

+ }
diff --git a/dashboard/src/lib/action/identity.ts b/dashboard/src/lib/action/identity.ts index b39e721..11ef3d9 100644 --- a/dashboard/src/lib/action/identity.ts +++ b/dashboard/src/lib/action/identity.ts @@ -15,6 +15,34 @@ import { eq, ilike, or, sql } from 'drizzle-orm'; import { checkPermission, requireSession } from '@/lib/action/authentication'; import { permission, relation } from '@/lib/permission'; +export async function getIdentity(id: string) { + + const session = await requireSession(); + const allowed = await checkPermission(permission.user.it, relation.access, session.identity!.id); + if (!allowed) { + throw Error('Unauthorised'); + } + + const identityApi = await getIdentityApi(); + const { data } = await identityApi.getIdentity({ id }); + + console.log('Got identity', data); + + return data; +} + + +export async function getIdentitySchema(id: string) { + + const identityApi = await getIdentityApi(); + const { data } = await identityApi.getIdentitySchema({ id: id }); + + console.log('Got identity schema'); + + return data; +} + + interface QueryIdentitiesProps { page: number, pageSize: number, @@ -131,6 +159,24 @@ export async function deleteIdentityCredential({ id, type }: DeleteIdentityCrede return data; } +export async function listIdentitySessions(id: string) { + + const session = await requireSession(); + const allowed = await checkPermission(permission.user.session, relation.access, session.identity!.id); + if (!allowed) { + throw Error('Unauthorised'); + } + + const identityApi = await getIdentityApi(); + const { data } = await identityApi.listIdentitySessions({ id }); + + console.log('Listed identity\'s sessions', data); + + revalidatePath('/user'); + + return data; +} + export async function deleteIdentitySessions(id: string) { const session = await requireSession(); diff --git a/dashboard/src/lib/permission.ts b/dashboard/src/lib/permission.ts index 49958b0..69a8523 100644 --- a/dashboard/src/lib/permission.ts +++ b/dashboard/src/lib/permission.ts @@ -10,6 +10,7 @@ export const permission = { link: 'admin.user.link', session: 'admin.user.session', state: 'admin.user.state', + trait: 'admin.user.trait', }, };