import React from 'react'; import { ErrorDisplay } from '@/components/error'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { IdentityTraits } from '@/components/identity/identity-traits'; import { KratosSchema } from '@/lib/forms/identity-form'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { UAParser } from 'ua-parser-js'; import { RecoveryIdentityAddress, VerifiableIdentityAddress } from '@ory/client'; import { Badge } from '@/components/ui/badge'; import { Check, X } from 'lucide-react'; import { IdentityActions } from '@/components/identity/identity-actions'; 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; verifiable_id?: string; verified?: boolean; verified_at?: string; value: string; via: string; } function mergeAddresses( recovery: RecoveryIdentityAddress[], verifiable: VerifiableIdentityAddress[], ): MergedAddress[] { const merged = [...recovery, ...verifiable]; return merged.reduce((acc: MergedAddress[], curr: any) => { const existingValue = acc.find(item => item.value && curr.value && item.value === curr.value); if (!existingValue) { let newEntry: MergedAddress; if (curr.status) { // status property exists only in verifiable addresses // expecting verifiable address newEntry = { verifiable_id: curr.id, verified: curr.verified, verified_at: curr.verified_at, value: curr.value, via: curr.via, } as MergedAddress; } else { // expecting recovery address newEntry = { recovery_id: curr.id, value: curr.value, via: curr.via, } as MergedAddress; } acc.push(newEntry); } else { const additionalValues = { recovery_id: existingValue.recovery_id, verifiable_id: curr.id, verified: curr.verified, verified_at: curr.verified_at, }; Object.assign(existingValue, additionalValues); } return acc; }, []); } export default async function UserDetailsPage({ params }: { params: Promise<{ id: string }> }) { const session = await requireSession(); const identityId = session.identity!.id; await requirePermission(permission.stack.dashboard, relation.access, identityId); const pmAccessUser = await checkPermission(permission.user.it, relation.access, identityId); if (!pmAccessUser) { return redirect('/user'); } const pmEditUser = await checkPermission(permission.user.it, relation.edit, identityId); const pmDeleteUser = await checkPermission(permission.user.it, relation.delete, identityId); const pmAccessUserTrait = await checkPermission(permission.user.trait, relation.access, identityId); const pmEditUserTraits = await checkPermission(permission.user.trait, relation.edit, identityId); const pmAccessUserAddress = await checkPermission(permission.user.address, relation.access, identityId); const pmAccessUserCredential = await checkPermission(permission.user.credential, 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); if (!detailIdentity) { return ; } if (!detailIdentity.verifiable_addresses || !detailIdentity.verifiable_addresses[0]) { return ; } const detailIdentitySessions = pmAccessUserSession && await listIdentitySessions(detailIdentityId); const detailIdentitySchema = await getIdentitySchema(detailIdentity.schema_id) .then((response) => response as KratosSchema); const addresses = mergeAddresses( detailIdentity.recovery_addresses ?? [], detailIdentity.verifiable_addresses ?? [], ); return (

{addresses[0].value}

{detailIdentity.id}

{ pmAccessUserTrait ? Traits All identity properties specified in the identity schema : } Actions Quick actions to manage the identity { pmAccessUserAddress ? Addresses All linked addresses for verification and recovery Type Value { addresses.map((address) => { return ( {address.value} {address.via} {address.verifiable_id && Verifiable { address.verified ? : } } {address.recovery_id && Recovery } ); }) }
: } { pmAccessUserCredential ? Credentials All authentication mechanisms registered with this identity : } { pmAccessUserSession ? Sessions See and manage all sessions of this identity { detailIdentitySessions ? OS Browser Active since { detailIdentitySessions.map((session) => { 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()} ); }) }
:

This user has no active sessions

}
: }
); }