mirror of
https://codeberg.org/MarkusThielker/next-ory.git
synced 2025-04-18 00:21:18 +00:00
commit
7961b50adb
22 changed files with 990 additions and 432 deletions
|
@ -1,334 +1,334 @@
|
|||
import { relations } from 'drizzle-orm/relations';
|
||||
import {
|
||||
continuityContainers,
|
||||
courierMessageDispatches,
|
||||
courierMessages,
|
||||
continuity_containers,
|
||||
courier_message_dispatches,
|
||||
courier_messages,
|
||||
identities,
|
||||
identityCredentialIdentifiers,
|
||||
identityCredentials,
|
||||
identityCredentialTypes,
|
||||
identityLoginCodes,
|
||||
identityRecoveryAddresses,
|
||||
identityRecoveryCodes,
|
||||
identityRecoveryTokens,
|
||||
identityRegistrationCodes,
|
||||
identityVerifiableAddresses,
|
||||
identityVerificationCodes,
|
||||
identityVerificationTokens,
|
||||
identity_credential_identifiers,
|
||||
identity_credential_types,
|
||||
identity_credentials,
|
||||
identity_login_codes,
|
||||
identity_recovery_addresses,
|
||||
identity_recovery_codes,
|
||||
identity_recovery_tokens,
|
||||
identity_registration_codes,
|
||||
identity_verifiable_addresses,
|
||||
identity_verification_codes,
|
||||
identity_verification_tokens,
|
||||
networks,
|
||||
selfserviceErrors,
|
||||
selfserviceLoginFlows,
|
||||
selfserviceRecoveryFlows,
|
||||
selfserviceRegistrationFlows,
|
||||
selfserviceSettingsFlows,
|
||||
selfserviceVerificationFlows,
|
||||
sessionDevices,
|
||||
selfservice_errors,
|
||||
selfservice_login_flows,
|
||||
selfservice_recovery_flows,
|
||||
selfservice_registration_flows,
|
||||
selfservice_settings_flows,
|
||||
selfservice_verification_flows,
|
||||
session_devices,
|
||||
sessions,
|
||||
} from './schema';
|
||||
|
||||
export const identityCredentialsRelations = relations(identityCredentials, ({ one, many }) => ({
|
||||
export const identityCredentialsRelations = relations(identity_credentials, ({ one, many }) => ({
|
||||
identity: one(identities, {
|
||||
fields: [identityCredentials.identityId],
|
||||
fields: [identity_credentials.identity_id],
|
||||
references: [identities.id],
|
||||
}),
|
||||
identityCredentialType: one(identityCredentialTypes, {
|
||||
fields: [identityCredentials.identityCredentialTypeId],
|
||||
references: [identityCredentialTypes.id],
|
||||
identityCredentialType: one(identity_credential_types, {
|
||||
fields: [identity_credentials.identity_credential_type_id],
|
||||
references: [identity_credential_types.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [identityCredentials.nid],
|
||||
fields: [identity_credentials.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
identityCredentialIdentifiers: many(identityCredentialIdentifiers),
|
||||
identityCredentialIdentifiers: many(identity_credential_identifiers),
|
||||
}));
|
||||
|
||||
export const identitiesRelations = relations(identities, ({ one, many }) => ({
|
||||
identityCredentials: many(identityCredentials),
|
||||
identityCredentials: many(identity_credentials),
|
||||
network: one(networks, {
|
||||
fields: [identities.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
identityVerifiableAddresses: many(identityVerifiableAddresses),
|
||||
selfserviceSettingsFlows: many(selfserviceSettingsFlows),
|
||||
continuityContainers: many(continuityContainers),
|
||||
identityVerifiableAddresses: many(identity_verifiable_addresses),
|
||||
selfserviceSettingsFlows: many(selfservice_settings_flows),
|
||||
continuityContainers: many(continuity_containers),
|
||||
sessions: many(sessions),
|
||||
identityRecoveryAddresses: many(identityRecoveryAddresses),
|
||||
selfserviceRecoveryFlows: many(selfserviceRecoveryFlows),
|
||||
identityRecoveryTokens: many(identityRecoveryTokens),
|
||||
identityRecoveryCodes: many(identityRecoveryCodes),
|
||||
identityLoginCodes: many(identityLoginCodes),
|
||||
identityRecoveryAddresses: many(identity_recovery_addresses),
|
||||
selfserviceRecoveryFlows: many(selfservice_recovery_flows),
|
||||
identityRecoveryTokens: many(identity_recovery_tokens),
|
||||
identityRecoveryCodes: many(identity_recovery_codes),
|
||||
identityLoginCodes: many(identity_login_codes),
|
||||
}));
|
||||
|
||||
export const identityCredentialTypesRelations = relations(identityCredentialTypes, ({ many }) => ({
|
||||
identityCredentials: many(identityCredentials),
|
||||
identityCredentialIdentifiers: many(identityCredentialIdentifiers),
|
||||
export const identityCredentialTypesRelations = relations(identity_credential_types, ({ many }) => ({
|
||||
identityCredentials: many(identity_credentials),
|
||||
identityCredentialIdentifiers: many(identity_credential_identifiers),
|
||||
}));
|
||||
|
||||
export const networksRelations = relations(networks, ({ many }) => ({
|
||||
identityCredentials: many(identityCredentials),
|
||||
selfserviceLoginFlows: many(selfserviceLoginFlows),
|
||||
selfserviceRegistrationFlows: many(selfserviceRegistrationFlows),
|
||||
identityCredentials: many(identity_credentials),
|
||||
selfserviceLoginFlows: many(selfservice_login_flows),
|
||||
selfserviceRegistrationFlows: many(selfservice_registration_flows),
|
||||
identities: many(identities),
|
||||
identityCredentialIdentifiers: many(identityCredentialIdentifiers),
|
||||
identityVerifiableAddresses: many(identityVerifiableAddresses),
|
||||
courierMessages: many(courierMessages),
|
||||
selfserviceErrors: many(selfserviceErrors),
|
||||
selfserviceVerificationFlows: many(selfserviceVerificationFlows),
|
||||
selfserviceSettingsFlows: many(selfserviceSettingsFlows),
|
||||
continuityContainers: many(continuityContainers),
|
||||
identityCredentialIdentifiers: many(identity_credential_identifiers),
|
||||
identityVerifiableAddresses: many(identity_verifiable_addresses),
|
||||
courierMessages: many(courier_messages),
|
||||
selfserviceErrors: many(selfservice_errors),
|
||||
selfserviceVerificationFlows: many(selfservice_verification_flows),
|
||||
selfserviceSettingsFlows: many(selfservice_settings_flows),
|
||||
continuityContainers: many(continuity_containers),
|
||||
sessions: many(sessions),
|
||||
identityRecoveryAddresses: many(identityRecoveryAddresses),
|
||||
identityVerificationTokens: many(identityVerificationTokens),
|
||||
selfserviceRecoveryFlows: many(selfserviceRecoveryFlows),
|
||||
identityRecoveryTokens: many(identityRecoveryTokens),
|
||||
identityRecoveryCodes: many(identityRecoveryCodes),
|
||||
sessionDevices: many(sessionDevices),
|
||||
identityVerificationCodes: many(identityVerificationCodes),
|
||||
courierMessageDispatches: many(courierMessageDispatches),
|
||||
identityLoginCodes: many(identityLoginCodes),
|
||||
identityRegistrationCodes: many(identityRegistrationCodes),
|
||||
identityRecoveryAddresses: many(identity_recovery_addresses),
|
||||
identityVerificationTokens: many(identity_verification_tokens),
|
||||
selfserviceRecoveryFlows: many(selfservice_recovery_flows),
|
||||
identityRecoveryTokens: many(identity_recovery_tokens),
|
||||
identityRecoveryCodes: many(identity_recovery_codes),
|
||||
sessionDevices: many(session_devices),
|
||||
identityVerificationCodes: many(identity_verification_codes),
|
||||
courierMessageDispatches: many(courier_message_dispatches),
|
||||
identityLoginCodes: many(identity_login_codes),
|
||||
identityRegistrationCodes: many(identity_registration_codes),
|
||||
}));
|
||||
|
||||
export const selfserviceLoginFlowsRelations = relations(selfserviceLoginFlows, ({ one, many }) => ({
|
||||
export const selfserviceLoginFlowsRelations = relations(selfservice_login_flows, ({ one, many }) => ({
|
||||
network: one(networks, {
|
||||
fields: [selfserviceLoginFlows.nid],
|
||||
fields: [selfservice_login_flows.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
identityLoginCodes: many(identityLoginCodes),
|
||||
identityLoginCodes: many(identity_login_codes),
|
||||
}));
|
||||
|
||||
export const selfserviceRegistrationFlowsRelations = relations(selfserviceRegistrationFlows, ({ one, many }) => ({
|
||||
export const selfserviceRegistrationFlowsRelations = relations(selfservice_registration_flows, ({ one, many }) => ({
|
||||
network: one(networks, {
|
||||
fields: [selfserviceRegistrationFlows.nid],
|
||||
fields: [selfservice_registration_flows.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
identityRegistrationCodes: many(identityRegistrationCodes),
|
||||
identityRegistrationCodes: many(identity_registration_codes),
|
||||
}));
|
||||
|
||||
export const identityCredentialIdentifiersRelations = relations(identityCredentialIdentifiers, ({ one }) => ({
|
||||
identityCredential: one(identityCredentials, {
|
||||
fields: [identityCredentialIdentifiers.identityCredentialId],
|
||||
references: [identityCredentials.id],
|
||||
export const identityCredentialIdentifiersRelations = relations(identity_credential_identifiers, ({ one }) => ({
|
||||
identityCredential: one(identity_credentials, {
|
||||
fields: [identity_credential_identifiers.identity_credential_id],
|
||||
references: [identity_credentials.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [identityCredentialIdentifiers.nid],
|
||||
fields: [identity_credential_identifiers.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
identityCredentialType: one(identityCredentialTypes, {
|
||||
fields: [identityCredentialIdentifiers.identityCredentialTypeId],
|
||||
references: [identityCredentialTypes.id],
|
||||
identityCredentialType: one(identity_credential_types, {
|
||||
fields: [identity_credential_identifiers.identity_credential_type_id],
|
||||
references: [identity_credential_types.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const identityVerifiableAddressesRelations = relations(identityVerifiableAddresses, ({ one, many }) => ({
|
||||
export const identityVerifiableAddressesRelations = relations(identity_verifiable_addresses, ({ one, many }) => ({
|
||||
identity: one(identities, {
|
||||
fields: [identityVerifiableAddresses.identityId],
|
||||
fields: [identity_verifiable_addresses.identity_id],
|
||||
references: [identities.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [identityVerifiableAddresses.nid],
|
||||
fields: [identity_verifiable_addresses.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
identityVerificationTokens: many(identityVerificationTokens),
|
||||
identityVerificationCodes: many(identityVerificationCodes),
|
||||
identityVerificationTokens: many(identity_verification_tokens),
|
||||
identityVerificationCodes: many(identity_verification_codes),
|
||||
}));
|
||||
|
||||
export const courierMessagesRelations = relations(courierMessages, ({ one, many }) => ({
|
||||
export const courierMessagesRelations = relations(courier_messages, ({ one, many }) => ({
|
||||
network: one(networks, {
|
||||
fields: [courierMessages.nid],
|
||||
fields: [courier_messages.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
courierMessageDispatches: many(courierMessageDispatches),
|
||||
courierMessageDispatches: many(courier_message_dispatches),
|
||||
}));
|
||||
|
||||
export const selfserviceErrorsRelations = relations(selfserviceErrors, ({ one }) => ({
|
||||
export const selfserviceErrorsRelations = relations(selfservice_errors, ({ one }) => ({
|
||||
network: one(networks, {
|
||||
fields: [selfserviceErrors.nid],
|
||||
fields: [selfservice_errors.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const selfserviceVerificationFlowsRelations = relations(selfserviceVerificationFlows, ({ one, many }) => ({
|
||||
export const selfserviceVerificationFlowsRelations = relations(selfservice_verification_flows, ({ one, many }) => ({
|
||||
network: one(networks, {
|
||||
fields: [selfserviceVerificationFlows.nid],
|
||||
fields: [selfservice_verification_flows.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
identityVerificationTokens: many(identityVerificationTokens),
|
||||
identityVerificationCodes: many(identityVerificationCodes),
|
||||
identityVerificationTokens: many(identity_verification_tokens),
|
||||
identityVerificationCodes: many(identity_verification_codes),
|
||||
}));
|
||||
|
||||
export const selfserviceSettingsFlowsRelations = relations(selfserviceSettingsFlows, ({ one }) => ({
|
||||
export const selfserviceSettingsFlowsRelations = relations(selfservice_settings_flows, ({ one }) => ({
|
||||
identity: one(identities, {
|
||||
fields: [selfserviceSettingsFlows.identityId],
|
||||
fields: [selfservice_settings_flows.identity_id],
|
||||
references: [identities.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [selfserviceSettingsFlows.nid],
|
||||
fields: [selfservice_settings_flows.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const continuityContainersRelations = relations(continuityContainers, ({ one }) => ({
|
||||
export const continuityContainersRelations = relations(continuity_containers, ({ one }) => ({
|
||||
identity: one(identities, {
|
||||
fields: [continuityContainers.identityId],
|
||||
fields: [continuity_containers.identity_id],
|
||||
references: [identities.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [continuityContainers.nid],
|
||||
fields: [continuity_containers.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const sessionsRelations = relations(sessions, ({ one, many }) => ({
|
||||
identity: one(identities, {
|
||||
fields: [sessions.identityId],
|
||||
fields: [sessions.identity_id],
|
||||
references: [identities.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [sessions.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
sessionDevices: many(sessionDevices),
|
||||
sessionDevices: many(session_devices),
|
||||
}));
|
||||
|
||||
export const identityRecoveryAddressesRelations = relations(identityRecoveryAddresses, ({ one, many }) => ({
|
||||
export const identityRecoveryAddressesRelations = relations(identity_recovery_addresses, ({ one, many }) => ({
|
||||
identity: one(identities, {
|
||||
fields: [identityRecoveryAddresses.identityId],
|
||||
fields: [identity_recovery_addresses.identity_id],
|
||||
references: [identities.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [identityRecoveryAddresses.nid],
|
||||
fields: [identity_recovery_addresses.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
identityRecoveryTokens: many(identityRecoveryTokens),
|
||||
identityRecoveryCodes: many(identityRecoveryCodes),
|
||||
identityRecoveryTokens: many(identity_recovery_tokens),
|
||||
identityRecoveryCodes: many(identity_recovery_codes),
|
||||
}));
|
||||
|
||||
export const identityVerificationTokensRelations = relations(identityVerificationTokens, ({ one }) => ({
|
||||
identityVerifiableAddress: one(identityVerifiableAddresses, {
|
||||
fields: [identityVerificationTokens.identityVerifiableAddressId],
|
||||
references: [identityVerifiableAddresses.id],
|
||||
export const identityVerificationTokensRelations = relations(identity_verification_tokens, ({ one }) => ({
|
||||
identityVerifiableAddress: one(identity_verifiable_addresses, {
|
||||
fields: [identity_verification_tokens.identity_verifiable_address_id],
|
||||
references: [identity_verifiable_addresses.id],
|
||||
}),
|
||||
selfserviceVerificationFlow: one(selfserviceVerificationFlows, {
|
||||
fields: [identityVerificationTokens.selfserviceVerificationFlowId],
|
||||
references: [selfserviceVerificationFlows.id],
|
||||
selfserviceVerificationFlow: one(selfservice_verification_flows, {
|
||||
fields: [identity_verification_tokens.selfservice_verification_flow_id],
|
||||
references: [selfservice_verification_flows.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [identityVerificationTokens.nid],
|
||||
fields: [identity_verification_tokens.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const selfserviceRecoveryFlowsRelations = relations(selfserviceRecoveryFlows, ({ one, many }) => ({
|
||||
export const selfserviceRecoveryFlowsRelations = relations(selfservice_recovery_flows, ({ one, many }) => ({
|
||||
identity: one(identities, {
|
||||
fields: [selfserviceRecoveryFlows.recoveredIdentityId],
|
||||
fields: [selfservice_recovery_flows.recovered_identity_id],
|
||||
references: [identities.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [selfserviceRecoveryFlows.nid],
|
||||
fields: [selfservice_recovery_flows.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
identityRecoveryTokens: many(identityRecoveryTokens),
|
||||
identityRecoveryCodes: many(identityRecoveryCodes),
|
||||
identityRecoveryTokens: many(identity_recovery_tokens),
|
||||
identityRecoveryCodes: many(identity_recovery_codes),
|
||||
}));
|
||||
|
||||
export const identityRecoveryTokensRelations = relations(identityRecoveryTokens, ({ one }) => ({
|
||||
selfserviceRecoveryFlow: one(selfserviceRecoveryFlows, {
|
||||
fields: [identityRecoveryTokens.selfserviceRecoveryFlowId],
|
||||
references: [selfserviceRecoveryFlows.id],
|
||||
export const identityRecoveryTokensRelations = relations(identity_recovery_tokens, ({ one }) => ({
|
||||
selfserviceRecoveryFlow: one(selfservice_recovery_flows, {
|
||||
fields: [identity_recovery_tokens.selfservice_recovery_flow_id],
|
||||
references: [selfservice_recovery_flows.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [identityRecoveryTokens.nid],
|
||||
fields: [identity_recovery_tokens.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
identityRecoveryAddress: one(identityRecoveryAddresses, {
|
||||
fields: [identityRecoveryTokens.identityRecoveryAddressId],
|
||||
references: [identityRecoveryAddresses.id],
|
||||
identityRecoveryAddress: one(identity_recovery_addresses, {
|
||||
fields: [identity_recovery_tokens.identity_recovery_address_id],
|
||||
references: [identity_recovery_addresses.id],
|
||||
}),
|
||||
identity: one(identities, {
|
||||
fields: [identityRecoveryTokens.identityId],
|
||||
fields: [identity_recovery_tokens.identity_id],
|
||||
references: [identities.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const identityRecoveryCodesRelations = relations(identityRecoveryCodes, ({ one }) => ({
|
||||
identityRecoveryAddress: one(identityRecoveryAddresses, {
|
||||
fields: [identityRecoveryCodes.identityRecoveryAddressId],
|
||||
references: [identityRecoveryAddresses.id],
|
||||
export const identityRecoveryCodesRelations = relations(identity_recovery_codes, ({ one }) => ({
|
||||
identityRecoveryAddress: one(identity_recovery_addresses, {
|
||||
fields: [identity_recovery_codes.identity_recovery_address_id],
|
||||
references: [identity_recovery_addresses.id],
|
||||
}),
|
||||
selfserviceRecoveryFlow: one(selfserviceRecoveryFlows, {
|
||||
fields: [identityRecoveryCodes.selfserviceRecoveryFlowId],
|
||||
references: [selfserviceRecoveryFlows.id],
|
||||
selfserviceRecoveryFlow: one(selfservice_recovery_flows, {
|
||||
fields: [identity_recovery_codes.selfservice_recovery_flow_id],
|
||||
references: [selfservice_recovery_flows.id],
|
||||
}),
|
||||
identity: one(identities, {
|
||||
fields: [identityRecoveryCodes.identityId],
|
||||
fields: [identity_recovery_codes.identity_id],
|
||||
references: [identities.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [identityRecoveryCodes.nid],
|
||||
fields: [identity_recovery_codes.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const sessionDevicesRelations = relations(sessionDevices, ({ one }) => ({
|
||||
export const sessionDevicesRelations = relations(session_devices, ({ one }) => ({
|
||||
session: one(sessions, {
|
||||
fields: [sessionDevices.sessionId],
|
||||
fields: [session_devices.session_id],
|
||||
references: [sessions.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [sessionDevices.nid],
|
||||
fields: [session_devices.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const identityVerificationCodesRelations = relations(identityVerificationCodes, ({ one }) => ({
|
||||
identityVerifiableAddress: one(identityVerifiableAddresses, {
|
||||
fields: [identityVerificationCodes.identityVerifiableAddressId],
|
||||
references: [identityVerifiableAddresses.id],
|
||||
export const identityVerificationCodesRelations = relations(identity_verification_codes, ({ one }) => ({
|
||||
identityVerifiableAddress: one(identity_verifiable_addresses, {
|
||||
fields: [identity_verification_codes.identity_verifiable_address_id],
|
||||
references: [identity_verifiable_addresses.id],
|
||||
}),
|
||||
selfserviceVerificationFlow: one(selfserviceVerificationFlows, {
|
||||
fields: [identityVerificationCodes.selfserviceVerificationFlowId],
|
||||
references: [selfserviceVerificationFlows.id],
|
||||
selfserviceVerificationFlow: one(selfservice_verification_flows, {
|
||||
fields: [identity_verification_codes.selfservice_verification_flow_id],
|
||||
references: [selfservice_verification_flows.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [identityVerificationCodes.nid],
|
||||
fields: [identity_verification_codes.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const courierMessageDispatchesRelations = relations(courierMessageDispatches, ({ one }) => ({
|
||||
courierMessage: one(courierMessages, {
|
||||
fields: [courierMessageDispatches.messageId],
|
||||
references: [courierMessages.id],
|
||||
export const courierMessageDispatchesRelations = relations(courier_message_dispatches, ({ one }) => ({
|
||||
courierMessage: one(courier_messages, {
|
||||
fields: [courier_message_dispatches.message_id],
|
||||
references: [courier_messages.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [courierMessageDispatches.nid],
|
||||
fields: [courier_message_dispatches.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const identityLoginCodesRelations = relations(identityLoginCodes, ({ one }) => ({
|
||||
selfserviceLoginFlow: one(selfserviceLoginFlows, {
|
||||
fields: [identityLoginCodes.selfserviceLoginFlowId],
|
||||
references: [selfserviceLoginFlows.id],
|
||||
export const identityLoginCodesRelations = relations(identity_login_codes, ({ one }) => ({
|
||||
selfserviceLoginFlow: one(selfservice_login_flows, {
|
||||
fields: [identity_login_codes.selfservice_login_flow_id],
|
||||
references: [selfservice_login_flows.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [identityLoginCodes.nid],
|
||||
fields: [identity_login_codes.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
identity: one(identities, {
|
||||
fields: [identityLoginCodes.identityId],
|
||||
fields: [identity_login_codes.identity_id],
|
||||
references: [identities.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const identityRegistrationCodesRelations = relations(identityRegistrationCodes, ({ one }) => ({
|
||||
selfserviceRegistrationFlow: one(selfserviceRegistrationFlows, {
|
||||
fields: [identityRegistrationCodes.selfserviceRegistrationFlowId],
|
||||
references: [selfserviceRegistrationFlows.id],
|
||||
export const identityRegistrationCodesRelations = relations(identity_registration_codes, ({ one }) => ({
|
||||
selfserviceRegistrationFlow: one(selfservice_registration_flows, {
|
||||
fields: [identity_registration_codes.selfservice_registration_flow_id],
|
||||
references: [selfservice_registration_flows.id],
|
||||
}),
|
||||
network: one(networks, {
|
||||
fields: [identityRegistrationCodes.nid],
|
||||
fields: [identity_registration_codes.nid],
|
||||
references: [networks.id],
|
||||
}),
|
||||
}));
|
|
@ -1,40 +1,21 @@
|
|||
import { getHydraMetadataApi, getKetoMetadataApi, getKratosMetadataApi } from '@/ory/sdk/server';
|
||||
import { MetadataApiReady, StatusCard } from '@/components/status-card';
|
||||
import { StatusCard } from '@/components/status-card';
|
||||
import { hydraMetadata, ketoMetadata, kratosMetadata } from '@/lib/action/metadata';
|
||||
import { checkPermission, requirePermission, requireSession } from '@/lib/action/authentication';
|
||||
import InsufficientPermission from '@/components/insufficient-permission';
|
||||
import { permission, relation } from '@/lib/permission';
|
||||
|
||||
export default async function RootPage() {
|
||||
|
||||
const kratosMetadataApi = await getKratosMetadataApi();
|
||||
const kratosVersion = await kratosMetadataApi.getVersion()
|
||||
.then(res => res.data.version)
|
||||
.catch(() => undefined);
|
||||
const kratosStatus = await fetch(process.env.ORY_KRATOS_ADMIN_URL + '/health/ready')
|
||||
.then((response) => response.json() as MetadataApiReady)
|
||||
.catch(() => {
|
||||
return { errors: ['No instance running'] } as MetadataApiReady;
|
||||
});
|
||||
const session = await requireSession();
|
||||
const identityId = session.identity!.id;
|
||||
|
||||
await requirePermission(permission.stack.dashboard, relation.access, identityId);
|
||||
|
||||
const hydraMetadataApi = await getHydraMetadataApi();
|
||||
const hydraVersion = await hydraMetadataApi.getVersion()
|
||||
.then(res => res.data.version)
|
||||
.catch(() => undefined);
|
||||
const hydraStatus = await fetch(process.env.ORY_HYDRA_ADMIN_URL + '/health/ready')
|
||||
.then((response) => response.json() as MetadataApiReady)
|
||||
.catch(() => {
|
||||
return { errors: ['No instance running'] } as MetadataApiReady;
|
||||
});
|
||||
|
||||
|
||||
const ketoMetadataApi = await getKetoMetadataApi();
|
||||
const ketoVersion = await ketoMetadataApi.getVersion()
|
||||
.then(res => res.data.version)
|
||||
.catch(() => undefined);
|
||||
const ketoStatus = await fetch(process.env.ORY_KETO_ADMIN_URL + '/health/ready')
|
||||
.then((response) => response.json() as MetadataApiReady)
|
||||
.catch(() => {
|
||||
return { errors: ['No instance running'] } as MetadataApiReady;
|
||||
});
|
||||
const pmAccessStackStatus = await checkPermission(permission.stack.status, relation.access, identityId);
|
||||
|
||||
const kratos = pmAccessStackStatus && await kratosMetadata();
|
||||
const hydra = pmAccessStackStatus && await hydraMetadata();
|
||||
const keto = pmAccessStackStatus && await ketoMetadata();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-4">
|
||||
|
@ -43,25 +24,46 @@ export default async function RootPage() {
|
|||
<p className="text-lg font-light">See the list of all applications in your stack</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
{
|
||||
!pmAccessStackStatus && (
|
||||
<InsufficientPermission
|
||||
permission={permission.stack.status}
|
||||
relation="access"
|
||||
identityId={identityId}
|
||||
classNames="col-span-1 md:col-span-4"
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
kratos && (
|
||||
<StatusCard
|
||||
title="Ory Kratos"
|
||||
version={kratosVersion}
|
||||
version={kratos.version}
|
||||
name="Kratos"
|
||||
status={kratosStatus}
|
||||
status={kratos.status}
|
||||
className="flex-1"/>
|
||||
)
|
||||
}
|
||||
{
|
||||
hydra && (
|
||||
<StatusCard
|
||||
title="Ory Hydra"
|
||||
version={hydraVersion}
|
||||
version={hydra.version}
|
||||
name="Hydra"
|
||||
status={hydraStatus}
|
||||
status={hydra.status}
|
||||
className="flex-1"/>
|
||||
)
|
||||
}
|
||||
{
|
||||
keto && (
|
||||
<StatusCard
|
||||
title="Ory Keto"
|
||||
version={ketoVersion}
|
||||
version={keto.version}
|
||||
name="Keto"
|
||||
status={ketoStatus}
|
||||
status={keto.status}
|
||||
className="flex-1"/>
|
||||
<div className="flex-1"/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -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';
|
||||
|
@ -11,6 +10,11 @@ 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;
|
||||
|
@ -76,70 +80,102 @@ function mergeAddresses(
|
|||
|
||||
export default async function UserDetailsPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
|
||||
const identityId = (await params).id;
|
||||
const session = await requireSession();
|
||||
const identityId = session.identity!.id;
|
||||
|
||||
const identityApi = await getIdentityApi();
|
||||
const identity = await identityApi.getIdentity({ id: identityId })
|
||||
.then((response) => {
|
||||
console.log('identity', response.data);
|
||||
return response.data;
|
||||
})
|
||||
.catch(() => {
|
||||
console.log('Identity not found');
|
||||
});
|
||||
await requirePermission(permission.stack.dashboard, relation.access, identityId);
|
||||
|
||||
const sessions = await identityApi.listIdentitySessions({ id: identityId })
|
||||
.then((response) => response.data)
|
||||
.catch(() => {
|
||||
console.log('No sessions found');
|
||||
});
|
||||
|
||||
if (!identity) {
|
||||
return <ErrorDisplay
|
||||
title="Identity not found"
|
||||
message={`The requested identity with id ${identityId} does not exist`}/>;
|
||||
const pmAccessUser = await checkPermission(permission.user.it, relation.access, identityId);
|
||||
if (!pmAccessUser) {
|
||||
return redirect('/user');
|
||||
}
|
||||
|
||||
if (!identity.verifiable_addresses || !identity.verifiable_addresses[0]) {
|
||||
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 <ErrorDisplay
|
||||
title="Identity not found"
|
||||
message={`The requested identity with id ${detailIdentityId} does not exist`}/>;
|
||||
}
|
||||
|
||||
if (!detailIdentity.verifiable_addresses || !detailIdentity.verifiable_addresses[0]) {
|
||||
return <ErrorDisplay
|
||||
title="No verifiable adress"
|
||||
message="The identity you are trying to see exists but has no identifiable address"/>;
|
||||
}
|
||||
|
||||
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 (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<p className="text-3xl font-bold leading-tight tracking-tight">{addresses[0].value}</p>
|
||||
<p className="text-lg font-light">{identity.id}</p>
|
||||
<p className="text-lg font-light">{detailIdentity.id}</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 xl:grid-cols-2 gap-4">
|
||||
{
|
||||
pmAccessUserTrait ?
|
||||
<Card className="row-span-3">
|
||||
<CardHeader>
|
||||
<CardTitle>Traits</CardTitle>
|
||||
<CardDescription>All identity properties specified in the identity schema</CardDescription>
|
||||
<CardDescription>All identity properties specified in the identity
|
||||
schema</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<IdentityTraits schema={identitySchema} identity={identity}/>
|
||||
<IdentityTraits
|
||||
schema={detailIdentitySchema}
|
||||
identity={detailIdentity}
|
||||
disabled={!pmEditUserTraits}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
:
|
||||
<InsufficientPermission
|
||||
permission={permission.user.trait}
|
||||
relation={relation.access}
|
||||
identityId={identityId}
|
||||
classNames="row-span-3"/>
|
||||
}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Actions</CardTitle>
|
||||
<CardDescription>Quick actions to manage the identity</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<IdentityActions identity={identity}/>
|
||||
<IdentityActions
|
||||
identity={detailIdentity}
|
||||
permissions={{
|
||||
pmDeleteUser,
|
||||
pmEditUserState,
|
||||
pmDeleteUserSession,
|
||||
pmCreateUserCode,
|
||||
pmCreateUserLink,
|
||||
}}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{
|
||||
pmAccessUserAddress ?
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Addresses</CardTitle>
|
||||
|
@ -185,21 +221,40 @@ export default async function UserDetailsPage({ params }: { params: Promise<{ id
|
|||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
:
|
||||
<InsufficientPermission
|
||||
permission={permission.user.address}
|
||||
relation={relation.access}
|
||||
identityId={identityId}/>
|
||||
}
|
||||
{
|
||||
pmAccessUserCredential ?
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Credentials</CardTitle>
|
||||
<CardDescription>All authentication mechanisms registered with this identity</CardDescription>
|
||||
<CardDescription>All authentication mechanisms registered with this
|
||||
identity</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<IdentityCredentials identity={identity}/>
|
||||
<IdentityCredentials identity={detailIdentity}/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
:
|
||||
<InsufficientPermission
|
||||
permission={permission.user.credential}
|
||||
relation={relation.access}
|
||||
identityId={identityId}/>
|
||||
}
|
||||
{
|
||||
pmAccessUserSession ?
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Sessions</CardTitle>
|
||||
<CardDescription>See and manage all sessions of this identity</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{
|
||||
detailIdentitySessions ?
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
|
@ -210,8 +265,7 @@ export default async function UserDetailsPage({ params }: { params: Promise<{ id
|
|||
</TableHeader>
|
||||
<TableBody>
|
||||
{
|
||||
sessions ?
|
||||
sessions.map((session) => {
|
||||
detailIdentitySessions.map((session) => {
|
||||
|
||||
const device = session.devices![0];
|
||||
const parser = new UAParser(device.user_agent);
|
||||
|
@ -235,13 +289,20 @@ export default async function UserDetailsPage({ params }: { params: Promise<{ id
|
|||
</TableRow>
|
||||
);
|
||||
})
|
||||
:
|
||||
<ErrorDisplay title="No sessions" message=""/>
|
||||
}
|
||||
</TableBody>
|
||||
</Table>
|
||||
:
|
||||
<p>This user has no active sessions</p>
|
||||
}
|
||||
</CardContent>
|
||||
</Card>
|
||||
:
|
||||
<InsufficientPermission
|
||||
permission={permission.user.session}
|
||||
relation={relation.access}
|
||||
identityId={identityId}/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -33,9 +33,15 @@ interface IdentityDataTableProps {
|
|||
data: Identity[];
|
||||
page: number;
|
||||
query: string;
|
||||
permission: {
|
||||
pmEditUser: boolean;
|
||||
pmDeleteUser: boolean;
|
||||
pmEditUserState: boolean;
|
||||
pmDeleteUserSession: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export function IdentityDataTable({ data, page, query }: IdentityDataTableProps) {
|
||||
export function IdentityDataTable({ data, page, query, permission }: IdentityDataTableProps) {
|
||||
|
||||
const columns: ColumnDef<Identity>[] = [
|
||||
{
|
||||
|
@ -137,6 +143,7 @@ export function IdentityDataTable({ data, page, query }: IdentityDataTableProps)
|
|||
setCurrentIdentity(identity);
|
||||
setIdentitySessionVisible(true);
|
||||
}}
|
||||
disabled={!permission.pmDeleteUserSession}
|
||||
className="flex items-center space-x-2 text-red-500">
|
||||
<UserMinus className="h-4 w-4"/>
|
||||
<span>Delete sessions</span>
|
||||
|
@ -148,6 +155,7 @@ export function IdentityDataTable({ data, page, query }: IdentityDataTableProps)
|
|||
setCurrentIdentity(identity);
|
||||
setBlockIdentityVisible(true);
|
||||
}}
|
||||
disabled={!permission.pmEditUserState}
|
||||
className="flex items-center space-x-2 text-red-500">
|
||||
<UserX className="h-4 w-4"/>
|
||||
<span>Block identity</span>
|
||||
|
@ -160,6 +168,7 @@ export function IdentityDataTable({ data, page, query }: IdentityDataTableProps)
|
|||
setCurrentIdentity(identity);
|
||||
setUnblockIdentityVisible(true);
|
||||
}}
|
||||
disabled={!permission.pmEditUserState}
|
||||
className="flex items-center space-x-2 text-red-500">
|
||||
<UserCheck className="h-4 w-4"/>
|
||||
<span>Unblock identity</span>
|
||||
|
@ -170,6 +179,7 @@ export function IdentityDataTable({ data, page, query }: IdentityDataTableProps)
|
|||
setCurrentIdentity(identity);
|
||||
setDeleteIdentityVisible(true);
|
||||
}}
|
||||
disabled={!permission.pmDeleteUser}
|
||||
className="flex items-center space-x-2 text-red-500">
|
||||
<Trash className="h-4 w-4"/>
|
||||
<span>Delete identity</span>
|
||||
|
|
|
@ -3,6 +3,9 @@ import { IdentityDataTable } from '@/app/(inside)/user/data-table';
|
|||
import { SearchInput } from '@/components/search-input';
|
||||
import { queryIdentities } from '@/lib/action/identity';
|
||||
import { IdentityPagination } from '@/components/pagination';
|
||||
import { checkPermission, requirePermission, requireSession } from '@/lib/action/authentication';
|
||||
import InsufficientPermission from '@/components/insufficient-permission';
|
||||
import { permission, relation } from '@/lib/permission';
|
||||
|
||||
export default async function UserPage(
|
||||
{
|
||||
|
@ -12,6 +15,17 @@ export default async function UserPage(
|
|||
},
|
||||
) {
|
||||
|
||||
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);
|
||||
const pmEditUser = await checkPermission(permission.user.it, relation.edit, identityId);
|
||||
const pmDeleteUser = await checkPermission(permission.user.it, relation.delete, identityId);
|
||||
const pmEditUserState = await checkPermission(permission.user.state, relation.edit, identityId);
|
||||
const pmDeleteUserSession = await checkPermission(permission.user.session, relation.delete, identityId);
|
||||
|
||||
const params = await searchParams;
|
||||
|
||||
const page = params.page ? Number(params.page) : 1;
|
||||
|
@ -20,7 +34,7 @@ export default async function UserPage(
|
|||
let pageSize = 50;
|
||||
let paginationRange = 11;
|
||||
|
||||
const { data, itemCount, pageCount } = await queryIdentities({ page, pageSize, query });
|
||||
const users = pmAccessUser && await queryIdentities(page, pageSize, query);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
|
@ -31,23 +45,45 @@ export default async function UserPage(
|
|||
</p>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{
|
||||
!pmAccessUser && (
|
||||
<InsufficientPermission
|
||||
permission={permission.user.it}
|
||||
relation={relation.access}
|
||||
identityId={identityId}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
pmAccessUser && users && (
|
||||
<>
|
||||
<SearchInput
|
||||
value={query}
|
||||
pageParamKey="page"
|
||||
queryParamKey="query"
|
||||
placeholder="Search for addresses and traits"/>
|
||||
<div>
|
||||
<p className="text-xs text-neutral-500">{itemCount} item{itemCount && itemCount > 1 ? 's' : ''} found</p>
|
||||
<p className="text-xs text-neutral-500">{users.itemCount} item{users.itemCount && users.itemCount > 1 ? 's' : ''} found</p>
|
||||
<IdentityDataTable
|
||||
data={data}
|
||||
data={users.data}
|
||||
page={page}
|
||||
query={query}/>
|
||||
query={query}
|
||||
permission={{
|
||||
pmEditUser: pmEditUser,
|
||||
pmDeleteUser: pmDeleteUser,
|
||||
pmEditUserState: pmEditUserState,
|
||||
pmDeleteUserSession: pmDeleteUserSession,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<IdentityPagination
|
||||
page={page}
|
||||
pageCount={pageCount}
|
||||
pageCount={users.pageCount}
|
||||
pageParamKey="page"
|
||||
paginationRange={paginationRange}/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -15,6 +15,7 @@ interface DynamicFormProps<T extends FieldValues> {
|
|||
onValid: SubmitHandler<T>,
|
||||
onInvalid: SubmitErrorHandler<T>,
|
||||
submitLabel?: string,
|
||||
disabled?: boolean,
|
||||
}
|
||||
|
||||
export function DynamicForm<T extends FieldValues>(
|
||||
|
@ -25,6 +26,7 @@ export function DynamicForm<T extends FieldValues>(
|
|||
onValid,
|
||||
onInvalid,
|
||||
submitLabel,
|
||||
disabled,
|
||||
}: DynamicFormProps<T>,
|
||||
) {
|
||||
|
||||
|
@ -48,7 +50,7 @@ export function DynamicForm<T extends FieldValues>(
|
|||
key={fullFieldName}
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
||||
<Checkbox {...field} checked={field.value}/>
|
||||
<Checkbox {...field} disabled={disabled} checked={field.value}/>
|
||||
<FormLabel>{key}</FormLabel>
|
||||
</FormItem>
|
||||
)}
|
||||
|
@ -65,7 +67,7 @@ export function DynamicForm<T extends FieldValues>(
|
|||
<FormItem>
|
||||
<FormLabel>{value.title}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={value.title} {...field} />
|
||||
<Input placeholder={value.title} {...field} disabled={disabled}/>
|
||||
</FormControl>
|
||||
<FormDescription>{value.description}</FormDescription>
|
||||
</FormItem>
|
||||
|
@ -87,7 +89,7 @@ export function DynamicForm<T extends FieldValues>(
|
|||
<Button
|
||||
key="submit"
|
||||
type="submit"
|
||||
disabled={!form.formState.isDirty}
|
||||
disabled={!form.formState.isDirty || disabled}
|
||||
>
|
||||
{submitLabel ?? 'Submit'}
|
||||
</Button>
|
||||
|
|
|
@ -28,12 +28,21 @@ import { Input } from '@/components/ui/input';
|
|||
import { Label } from '@/components/ui/label';
|
||||
|
||||
interface IdentityActionProps {
|
||||
identity: Identity;
|
||||
identity: Identity,
|
||||
permissions: {
|
||||
pmDeleteUser: boolean;
|
||||
pmEditUserState: boolean;
|
||||
pmDeleteUserSession: boolean;
|
||||
pmCreateUserCode: boolean;
|
||||
pmCreateUserLink: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
export function IdentityActions({ identity }: IdentityActionProps,
|
||||
export function IdentityActions({ identity, permissions }: IdentityActionProps,
|
||||
) {
|
||||
|
||||
console.log('IdentityActions', 'Permissions', permissions);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const [dialogVisible, setDialogVisible] = useState(false);
|
||||
|
@ -122,7 +131,10 @@ export function IdentityActions({ identity }: IdentityActionProps,
|
|||
dialogDescription="Are you sure you want to create a recovery code for this identity?"
|
||||
dialogButtonSubmit="Create code"
|
||||
>
|
||||
<Button className="mr-2" size="icon">
|
||||
<Button
|
||||
disabled={!permissions.pmCreateUserCode}
|
||||
className="mr-2"
|
||||
size="icon">
|
||||
<Key className="h-4"/>
|
||||
</Button>
|
||||
</ConfirmationDialogWrapper>
|
||||
|
@ -142,7 +154,10 @@ export function IdentityActions({ identity }: IdentityActionProps,
|
|||
dialogDescription="Are you sure you want to create a recovery link for this identity?"
|
||||
dialogButtonSubmit="Create link"
|
||||
>
|
||||
<Button className="mr-2" size="icon">
|
||||
<Button
|
||||
disabled={!permissions.pmCreateUserLink}
|
||||
className="mr-2"
|
||||
size="icon">
|
||||
<Link className="h-4"/>
|
||||
</Button>
|
||||
</ConfirmationDialogWrapper>
|
||||
|
@ -160,7 +175,10 @@ export function IdentityActions({ identity }: IdentityActionProps,
|
|||
dialogDescription="Are you sure you want to deactivate this identity? The user will not be able to sign-in or use any active session until re-activation!"
|
||||
dialogButtonSubmit="Deactivate"
|
||||
>
|
||||
<Button className="mr-2" size="icon">
|
||||
<Button
|
||||
disabled={!permissions.pmEditUserState}
|
||||
className="mr-2"
|
||||
size="icon">
|
||||
<UserX className="h-4"/>
|
||||
</Button>
|
||||
</ConfirmationDialogWrapper>
|
||||
|
@ -176,7 +194,10 @@ export function IdentityActions({ identity }: IdentityActionProps,
|
|||
dialogDescription="Are you sure you want to activate this identity?"
|
||||
dialogButtonSubmit="Activate"
|
||||
>
|
||||
<Button className="mr-2" size="icon">
|
||||
<Button
|
||||
disabled={!permissions.pmEditUserState}
|
||||
className="mr-2"
|
||||
size="icon">
|
||||
<UserCheck className="h-4"/>
|
||||
</Button>
|
||||
</ConfirmationDialogWrapper>
|
||||
|
@ -194,7 +215,10 @@ export function IdentityActions({ identity }: IdentityActionProps,
|
|||
dialogButtonSubmit="Invalidate sessions"
|
||||
dialogButtonSubmitProps={{ variant: 'destructive' }}
|
||||
>
|
||||
<Button className="mr-2" size="icon">
|
||||
<Button
|
||||
disabled={!permissions.pmDeleteUserSession}
|
||||
className="mr-2"
|
||||
size="icon">
|
||||
<UserMinus className="h-4"/>
|
||||
</Button>
|
||||
</ConfirmationDialogWrapper>
|
||||
|
@ -214,7 +238,10 @@ export function IdentityActions({ identity }: IdentityActionProps,
|
|||
dialogButtonSubmit="Delete identity"
|
||||
dialogButtonSubmitProps={{ variant: 'destructive' }}
|
||||
>
|
||||
<Button className="mr-2" size="icon">
|
||||
<Button
|
||||
disabled={!permissions.pmDeleteUser}
|
||||
className="mr-2"
|
||||
size="icon">
|
||||
<Trash className="h-4"/>
|
||||
</Button>
|
||||
</ConfirmationDialogWrapper>
|
||||
|
|
|
@ -36,7 +36,7 @@ export function IdentityCredentials({ identity }: IdentityCredentialsProps) {
|
|||
(
|
||||
<ConfirmationDialogWrapper
|
||||
onSubmit={async () => {
|
||||
deleteIdentityCredential({ id: identity.id, type: key as never })
|
||||
deleteIdentityCredential(identity.id, key as never)
|
||||
.then(() => toast.success(`Credential ${key} deleted`))
|
||||
.catch(() => toast.error(`Deleting credential ${key} failed`));
|
||||
}}
|
||||
|
|
|
@ -16,9 +16,10 @@ import { useState } from 'react';
|
|||
interface IdentityTraitFormProps {
|
||||
schema: KratosSchema;
|
||||
identity: Identity;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
export function IdentityTraits({ schema, identity }: IdentityTraitFormProps) {
|
||||
export function IdentityTraits({ schema, identity, disabled }: IdentityTraitFormProps) {
|
||||
|
||||
const [currentIdentity, setCurrentIdentity] = useState(identity);
|
||||
|
||||
|
@ -47,16 +48,16 @@ export function IdentityTraits({ schema, identity }: IdentityTraitFormProps) {
|
|||
delete traits['metadata_public'];
|
||||
delete traits['metadata_admin'];
|
||||
|
||||
updateIdentity({
|
||||
id: currentIdentity.id,
|
||||
body: {
|
||||
updateIdentity(
|
||||
currentIdentity.id,
|
||||
{
|
||||
schema_id: currentIdentity.schema_id,
|
||||
state: currentIdentity.state!,
|
||||
traits: traits,
|
||||
metadata_public: data.metadata_public,
|
||||
metadata_admin: data.metadata_admin,
|
||||
},
|
||||
})
|
||||
)
|
||||
.then((identity) => {
|
||||
setCurrentIdentity(identity);
|
||||
toast.success('Identity updated');
|
||||
|
@ -74,10 +75,11 @@ export function IdentityTraits({ schema, identity }: IdentityTraitFormProps) {
|
|||
return (
|
||||
<DynamicForm
|
||||
form={form}
|
||||
disabled={disabled}
|
||||
properties={schema.properties.traits.properties}
|
||||
onValid={onValid}
|
||||
onInvalid={onInvalid}
|
||||
submitLabel="Update Identity"
|
||||
submitLabel={disabled ? 'Insufficient permissions' : 'Update Identity'}
|
||||
>
|
||||
<FormField
|
||||
{...form.register('metadata_public')}
|
||||
|
@ -86,7 +88,7 @@ export function IdentityTraits({ schema, identity }: IdentityTraitFormProps) {
|
|||
<FormItem>
|
||||
<FormLabel>Public Metadata</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea placeholder="Public Metadata" {...field} />
|
||||
<Textarea placeholder="Public Metadata" {...field} disabled={disabled}/>
|
||||
</FormControl>
|
||||
<FormDescription>This has to be valid JSON</FormDescription>
|
||||
</FormItem>
|
||||
|
@ -99,7 +101,7 @@ export function IdentityTraits({ schema, identity }: IdentityTraitFormProps) {
|
|||
<FormItem>
|
||||
<FormLabel>Admin Metadata</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea placeholder="Admin Metadata" {...field} />
|
||||
<Textarea placeholder="Admin Metadata" {...field} disabled={disabled}/>
|
||||
</FormControl>
|
||||
<FormDescription>This has to be valid JSON</FormDescription>
|
||||
</FormItem>
|
||||
|
|
36
dashboard/src/components/insufficient-permission.tsx
Normal file
36
dashboard/src/components/insufficient-permission.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { Card, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
|
||||
interface InsufficientPermissionProps {
|
||||
permission: string;
|
||||
relation: string;
|
||||
identityId: string;
|
||||
classNames?: string;
|
||||
}
|
||||
|
||||
export default async function InsufficientPermission(
|
||||
{
|
||||
permission,
|
||||
relation,
|
||||
identityId,
|
||||
classNames,
|
||||
}: InsufficientPermissionProps,
|
||||
) {
|
||||
return (
|
||||
<Card className={classNames}>
|
||||
<CardHeader>
|
||||
<CardTitle>Insufficient Permission</CardTitle>
|
||||
<CardDescription>
|
||||
You are missing the permission to see this content.<br/>
|
||||
If you think this is an error, please contact your system administrator
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardFooter>
|
||||
<CardDescription className="text-xs">
|
||||
Permission: {permission}<br/>
|
||||
Relation: {relation}<br/>
|
||||
Identity: {identityId}
|
||||
</CardDescription>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
|
@ -50,7 +50,7 @@ export function StatusCard({ title, version, name, status, className }: StatusCa
|
|||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{
|
||||
status.errors.map((error) => <span>{error}</span>)
|
||||
status.errors.map((error) => <span key={error}>{error}</span>)
|
||||
}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
|
106
dashboard/src/lib/action/authentication.ts
Normal file
106
dashboard/src/lib/action/authentication.ts
Normal file
|
@ -0,0 +1,106 @@
|
|||
'use server';
|
||||
|
||||
import { getFrontendApi, getPermissionApi } from '@/ory/sdk/server';
|
||||
import { cookies } from 'next/headers';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
export async function getSession() {
|
||||
|
||||
const cookie = await cookies();
|
||||
|
||||
const frontendApi = await getFrontendApi();
|
||||
|
||||
return frontendApi
|
||||
.toSession({ cookie: 'ory_kratos_session=' + cookie.get('ory_kratos_session')?.value })
|
||||
.then((response) => response.data)
|
||||
.catch(() => null);
|
||||
}
|
||||
|
||||
export async function requireSession() {
|
||||
|
||||
const session = await getSession();
|
||||
|
||||
if (!session) {
|
||||
|
||||
// TODO: set return_to dynamically
|
||||
const url = process.env.NEXT_PUBLIC_AUTHENTICATION_NODE_URL +
|
||||
'/flow/login?return_to=' + process.env.NEXT_PUBLIC_DASHBOARD_NODE_URL;
|
||||
|
||||
console.log('Intercepted request with missing session');
|
||||
console.log('Redirecting client to: ', url);
|
||||
|
||||
redirect(url);
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
export async function checkRole(
|
||||
object: string,
|
||||
subjectId: string,
|
||||
) {
|
||||
|
||||
const permissionApi = await getPermissionApi();
|
||||
return permissionApi.checkPermission({
|
||||
namespace: 'roles',
|
||||
object,
|
||||
relation: 'member',
|
||||
subjectId,
|
||||
})
|
||||
.then(({ data: { allowed } }) => allowed)
|
||||
.catch(_ => false);
|
||||
}
|
||||
|
||||
export async function requireRole(
|
||||
object: string,
|
||||
subjectId: string,
|
||||
) {
|
||||
|
||||
const hasRole = await checkRole(object, subjectId);
|
||||
|
||||
if (!hasRole) {
|
||||
console.log(`Intercepted request with missing role ${object} for identity ${subjectId}`);
|
||||
redirect('/unauthorised');
|
||||
}
|
||||
|
||||
return hasRole;
|
||||
}
|
||||
|
||||
|
||||
export async function checkPermission(
|
||||
object: string,
|
||||
relation: string,
|
||||
subjectId: string,
|
||||
) {
|
||||
|
||||
const permissionApi = await getPermissionApi();
|
||||
return permissionApi.checkPermission({
|
||||
namespace: 'permissions',
|
||||
object,
|
||||
relation,
|
||||
subjectId,
|
||||
})
|
||||
.then(({ data: { allowed } }) => allowed)
|
||||
.catch(_ => false);
|
||||
}
|
||||
|
||||
export async function requirePermission(
|
||||
object: string,
|
||||
relation: string,
|
||||
subjectId: string,
|
||||
) {
|
||||
|
||||
const allowed = await checkPermission(
|
||||
object,
|
||||
relation,
|
||||
subjectId,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
console.log(`Intercepted request with insufficient permission (${object}#${relation}@${subjectId})`);
|
||||
redirect('/unauthorised');
|
||||
}
|
||||
|
||||
return allowed;
|
||||
}
|
|
@ -12,14 +12,43 @@ import {
|
|||
import { getDB } from '@/db';
|
||||
import { identities, identity_recovery_addresses, identity_verifiable_addresses } from '@/db/schema';
|
||||
import { eq, ilike, or, sql } from 'drizzle-orm';
|
||||
import { checkPermission, requireSession } from '@/lib/action/authentication';
|
||||
import { permission, relation } from '@/lib/permission';
|
||||
|
||||
interface QueryIdentitiesProps {
|
||||
page: number,
|
||||
pageSize: number,
|
||||
query?: string,
|
||||
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');
|
||||
}
|
||||
|
||||
export async function queryIdentities({ page, pageSize, query }: QueryIdentitiesProps) {
|
||||
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;
|
||||
}
|
||||
|
||||
export async function queryIdentities(page: number, pageSize: number, query?: string) {
|
||||
|
||||
const session = await requireSession();
|
||||
const allowed = await checkPermission(permission.user.it, relation.access, session.identity!.id);
|
||||
if (!allowed) {
|
||||
throw Error('Unauthorised');
|
||||
}
|
||||
|
||||
if (page < 1 || pageSize < 1) {
|
||||
return {
|
||||
|
@ -73,14 +102,14 @@ export async function queryIdentities({ page, pageSize, query }: QueryIdentities
|
|||
};
|
||||
}
|
||||
|
||||
export async function updateIdentity(id: string, body: UpdateIdentityBody) {
|
||||
|
||||
interface UpdatedIdentityProps {
|
||||
id: string;
|
||||
body: UpdateIdentityBody;
|
||||
const session = await requireSession();
|
||||
const allowed = await checkPermission(permission.user.it, relation.edit, session.identity!.id);
|
||||
if (!allowed) {
|
||||
throw Error('Unauthorised');
|
||||
}
|
||||
|
||||
export async function updateIdentity({ id, body }: UpdatedIdentityProps) {
|
||||
|
||||
const identityApi = await getIdentityApi();
|
||||
const { data } = await identityApi.updateIdentity({
|
||||
id: id,
|
||||
|
@ -94,12 +123,13 @@ export async function updateIdentity({ id, body }: UpdatedIdentityProps) {
|
|||
return data;
|
||||
}
|
||||
|
||||
interface DeleteIdentityCredentialProps {
|
||||
id: string;
|
||||
type: DeleteIdentityCredentialsTypeEnum;
|
||||
}
|
||||
export async function deleteIdentityCredential(id: string, type: DeleteIdentityCredentialsTypeEnum) {
|
||||
|
||||
export async function deleteIdentityCredential({ id, type }: DeleteIdentityCredentialProps) {
|
||||
const session = await requireSession();
|
||||
const allowed = await checkPermission(permission.user.credential, relation.delete, session.identity!.id);
|
||||
if (!allowed) {
|
||||
throw Error('Unauthorised');
|
||||
}
|
||||
|
||||
const identityApi = await getIdentityApi();
|
||||
const { data } = await identityApi.deleteIdentityCredentials({ id, type });
|
||||
|
@ -111,8 +141,30 @@ 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);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function deleteIdentitySessions(id: string) {
|
||||
|
||||
const session = await requireSession();
|
||||
const allowed = await checkPermission(permission.user.session, relation.delete, session.identity!.id);
|
||||
if (!allowed) {
|
||||
throw Error('Unauthorised');
|
||||
}
|
||||
|
||||
const identityApi = await getIdentityApi();
|
||||
const { data } = await identityApi.deleteIdentitySessions({ id });
|
||||
|
||||
|
@ -125,6 +177,12 @@ export async function deleteIdentitySessions(id: string) {
|
|||
|
||||
export async function createRecoveryCode(id: string) {
|
||||
|
||||
const session = await requireSession();
|
||||
const allowed = await checkPermission(permission.user.code, relation.create, session.identity!.id);
|
||||
if (!allowed) {
|
||||
throw Error('Unauthorised');
|
||||
}
|
||||
|
||||
const identityApi = await getIdentityApi();
|
||||
const { data } = await identityApi.createRecoveryCodeForIdentity({
|
||||
createRecoveryCodeForIdentityBody: {
|
||||
|
@ -139,6 +197,12 @@ export async function createRecoveryCode(id: string) {
|
|||
|
||||
export async function createRecoveryLink(id: string) {
|
||||
|
||||
const session = await requireSession();
|
||||
const allowed = await checkPermission(permission.user.link, relation.create, session.identity!.id);
|
||||
if (!allowed) {
|
||||
throw Error('Unauthorised');
|
||||
}
|
||||
|
||||
const identityApi = await getIdentityApi();
|
||||
const { data } = await identityApi.createRecoveryLinkForIdentity({
|
||||
createRecoveryLinkForIdentityBody: {
|
||||
|
@ -153,6 +217,12 @@ export async function createRecoveryLink(id: string) {
|
|||
|
||||
export async function blockIdentity(id: string) {
|
||||
|
||||
const session = await requireSession();
|
||||
const allowed = await checkPermission(permission.user.state, relation.edit, session.identity!.id);
|
||||
if (!allowed) {
|
||||
throw Error('Unauthorised');
|
||||
}
|
||||
|
||||
const identityApi = await getIdentityApi();
|
||||
const { data } = await identityApi.patchIdentity({
|
||||
id,
|
||||
|
@ -172,6 +242,12 @@ export async function blockIdentity(id: string) {
|
|||
|
||||
export async function unblockIdentity(id: string) {
|
||||
|
||||
const session = await requireSession();
|
||||
const allowed = await checkPermission(permission.user.state, relation.edit, session.identity!.id);
|
||||
if (!allowed) {
|
||||
throw Error('Unauthorised');
|
||||
}
|
||||
|
||||
const identityApi = await getIdentityApi();
|
||||
const { data } = await identityApi.patchIdentity({
|
||||
id,
|
||||
|
@ -191,6 +267,12 @@ export async function unblockIdentity(id: string) {
|
|||
|
||||
export async function deleteIdentity(id: string) {
|
||||
|
||||
const session = await requireSession();
|
||||
const allowed = await checkPermission(permission.user.it, relation.delete, session.identity!.id);
|
||||
if (!allowed) {
|
||||
throw Error('Unauthorised');
|
||||
}
|
||||
|
||||
const identityApi = await getIdentityApi();
|
||||
const { data } = await identityApi.deleteIdentity({ id });
|
||||
|
||||
|
|
84
dashboard/src/lib/action/metadata.ts
Normal file
84
dashboard/src/lib/action/metadata.ts
Normal file
|
@ -0,0 +1,84 @@
|
|||
'use server';
|
||||
|
||||
import { getHydraMetadataApi, getKetoMetadataApi, getKratosMetadataApi } from '@/ory/sdk/server';
|
||||
import { MetadataApiReady } from '@/components/status-card';
|
||||
import { checkPermission, requireSession } from '@/lib/action/authentication';
|
||||
import { permission, relation } from '@/lib/permission';
|
||||
|
||||
export async function kratosMetadata() {
|
||||
|
||||
const session = await requireSession();
|
||||
const allowed = await checkPermission(permission.stack.status, relation.access, session.identity!.id);
|
||||
if (!allowed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const api = await getKratosMetadataApi();
|
||||
|
||||
const version = await api.getVersion()
|
||||
.then(res => res.data.version)
|
||||
.catch(() => undefined);
|
||||
|
||||
const status = await fetch(process.env.ORY_KRATOS_ADMIN_URL + '/health/ready')
|
||||
.then((response) => response.json() as MetadataApiReady)
|
||||
.catch(() => {
|
||||
return { errors: ['No instance running'] } as MetadataApiReady;
|
||||
});
|
||||
|
||||
return {
|
||||
version,
|
||||
status,
|
||||
};
|
||||
}
|
||||
|
||||
export async function hydraMetadata() {
|
||||
|
||||
const session = await requireSession();
|
||||
const allowed = await checkPermission(permission.stack.status, relation.access, session.identity!.id);
|
||||
if (!allowed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const api = await getHydraMetadataApi();
|
||||
|
||||
const version = await api.getVersion()
|
||||
.then(res => res.data.version)
|
||||
.catch(() => undefined);
|
||||
|
||||
const status = await fetch(process.env.ORY_HYDRA_ADMIN_URL + '/health/ready')
|
||||
.then((response) => response.json() as MetadataApiReady)
|
||||
.catch(() => {
|
||||
return { errors: ['No instance running'] } as MetadataApiReady;
|
||||
});
|
||||
|
||||
return {
|
||||
version,
|
||||
status,
|
||||
};
|
||||
}
|
||||
|
||||
export async function ketoMetadata() {
|
||||
|
||||
const session = await requireSession();
|
||||
const allowed = await checkPermission(permission.stack.status, relation.access, session.identity!.id);
|
||||
if (!allowed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const api = await getKetoMetadataApi();
|
||||
|
||||
const version = await api.getVersion()
|
||||
.then(res => res.data.version)
|
||||
.catch(() => undefined);
|
||||
|
||||
const status = await fetch(process.env.ORY_KETO_ADMIN_URL + '/health/ready')
|
||||
.then((response) => response.json() as MetadataApiReady)
|
||||
.catch(() => {
|
||||
return { errors: ['No instance running'] } as MetadataApiReady;
|
||||
});
|
||||
|
||||
return {
|
||||
version,
|
||||
status,
|
||||
};
|
||||
}
|
23
dashboard/src/lib/permission.ts
Normal file
23
dashboard/src/lib/permission.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
export const permission = {
|
||||
stack: {
|
||||
dashboard: 'admin.stack.dashboard',
|
||||
status: 'admin.stack.status',
|
||||
},
|
||||
user: {
|
||||
it: 'admin.user',
|
||||
address: 'admin.user.address',
|
||||
code: 'admin.user.code',
|
||||
credential: 'admin.user.credential',
|
||||
link: 'admin.user.link',
|
||||
session: 'admin.user.session',
|
||||
state: 'admin.user.state',
|
||||
trait: 'admin.user.trait',
|
||||
},
|
||||
};
|
||||
|
||||
export const relation = {
|
||||
access: 'access',
|
||||
create: 'create',
|
||||
edit: 'edit',
|
||||
delete: 'delete',
|
||||
};
|
|
@ -1,47 +1,29 @@
|
|||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { cookies } from 'next/headers';
|
||||
import { getFrontendApi, getPermissionApi } from '@/ory/sdk/server';
|
||||
import { checkPermission, getSession } from '@/lib/action/authentication';
|
||||
import { permission, relation } from '@/lib/permission';
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
|
||||
const frontendApi = await getFrontendApi();
|
||||
const cookie = await cookies();
|
||||
|
||||
const session = await frontendApi
|
||||
.toSession({ cookie: 'ory_kratos_session=' + cookie.get('ory_kratos_session')?.value })
|
||||
.then((response) => response.data)
|
||||
.catch(() => null);
|
||||
// middleware can not work with requireSession, requireRole and
|
||||
// requirePermission due to the different redirect mechanisms in use!
|
||||
|
||||
const session = await getSession();
|
||||
if (!session) {
|
||||
|
||||
console.log('NO SESSION');
|
||||
console.log('middleware', 'MISSING SESSION');
|
||||
|
||||
const url = process.env.NEXT_PUBLIC_AUTHENTICATION_NODE_URL +
|
||||
'/flow/login?return_to=' +
|
||||
process.env.NEXT_PUBLIC_DASHBOARD_NODE_URL;
|
||||
|
||||
console.log('REDIRECT TO', url);
|
||||
|
||||
return NextResponse.redirect(url);
|
||||
console.log('middleware', 'REDIRECT TO', url);
|
||||
return NextResponse.redirect(url!);
|
||||
}
|
||||
|
||||
const permissionApi = await getPermissionApi();
|
||||
const isAdmin = await permissionApi.checkPermission({
|
||||
namespace: 'roles',
|
||||
object: 'admin',
|
||||
relation: 'member',
|
||||
subjectId: session!.identity!.id,
|
||||
})
|
||||
.then(({ data: { allowed } }) => {
|
||||
console.log('is_admin', session!.identity!.id, allowed);
|
||||
return allowed;
|
||||
})
|
||||
.catch((response) => {
|
||||
console.log('is_admin', session!.identity!.id, response, 'check failed');
|
||||
return false;
|
||||
});
|
||||
const allowed = await checkPermission(permission.stack.dashboard, relation.access, session.identity!.id);
|
||||
|
||||
if (isAdmin) {
|
||||
|
||||
if (allowed) {
|
||||
if (request.nextUrl.pathname === '/unauthorised') {
|
||||
return redirect('/', 'HAS PERMISSION BUT ACCESSING /unauthorized');
|
||||
}
|
||||
|
@ -55,9 +37,9 @@ export async function middleware(request: NextRequest) {
|
|||
}
|
||||
|
||||
function redirect(path: string, reason: string) {
|
||||
console.log(reason);
|
||||
console.log('middleware', reason);
|
||||
const url = `${process.env.NEXT_PUBLIC_DASHBOARD_NODE_URL}${path}`;
|
||||
console.log('REDIRECT TO', url);
|
||||
console.log('middleware', 'REDIRECT TO', url);
|
||||
return NextResponse.redirect(url!);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# this script adds a new oath client using the
|
||||
# this script adds a new OAuth client using the
|
||||
# Ory Hydra CLI and writes the client id and
|
||||
# client secret to the command line.
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# this script adds a new oath client using the
|
||||
# this script adds a new OAuth client using the
|
||||
# Ory Hydra CLI and uses the client to start
|
||||
# the Ory Hydra test application.
|
||||
|
||||
|
|
30
docker/ory-dev/keto-add-permission-to-role.sh
Normal file
30
docker/ory-dev/keto-add-permission-to-role.sh
Normal file
|
@ -0,0 +1,30 @@
|
|||
# this script creates a reference from the role to the permission you provide
|
||||
|
||||
# check if a identity id argument was provided
|
||||
if [ $# -ne 4 ]; then
|
||||
echo "Usage: $0 <object> <relation> <role> <role_relation>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# set user id variable
|
||||
OBJECT=$1
|
||||
RELATION=$2
|
||||
ROLE=$3
|
||||
ROLE_RELATION=$4
|
||||
|
||||
# execute curl to Ory Keto write endpoint
|
||||
curl --request PUT \
|
||||
--url http://localhost:4467/admin/relation-tuples \
|
||||
--data '{
|
||||
"namespace": "permissions",
|
||||
"object": "'"$OBJECT"'",
|
||||
"relation": "'"$RELATION"'",
|
||||
"subject_set": {
|
||||
"namespace": "roles",
|
||||
"object": "'"$ROLE"'",
|
||||
"relation": "'"$ROLE_RELATION"'"
|
||||
}
|
||||
}'
|
||||
|
||||
# write success response to terminal
|
||||
echo "Added relation Permissions:$OBJECT#$RELATION@(Roles:$ROLE#$RELATION)"
|
26
docker/ory-dev/keto-add-permission.sh
Normal file
26
docker/ory-dev/keto-add-permission.sh
Normal file
|
@ -0,0 +1,26 @@
|
|||
# this script gives the referenced identity the provided permission
|
||||
# make sure to provide the id of the identity
|
||||
|
||||
# check if a required arguments were provided
|
||||
if [ $# -ne 3 ]; then
|
||||
echo "Usage: $0 <object> <relation> <identity_id>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# set variables from input
|
||||
OBJECT=$1
|
||||
RELATION=$2
|
||||
IDENTITY_ID=$3
|
||||
|
||||
# execute curl to Ory Keto write endpoint
|
||||
curl --request PUT \
|
||||
--url http://localhost:4467/admin/relation-tuples \
|
||||
--data '{
|
||||
"namespace": "permissions",
|
||||
"object": "'"$OBJECT"'",
|
||||
"relation": "'"$RELATION"'",
|
||||
"subject_id": "'"$IDENTITY_ID"'"
|
||||
}'
|
||||
|
||||
# write success response to terminal
|
||||
echo "Added permission $OBJECT#$RELATION@$IDENTITY_ID"
|
47
docker/ory-dev/keto-init-admin-role.sh
Normal file
47
docker/ory-dev/keto-init-admin-role.sh
Normal file
|
@ -0,0 +1,47 @@
|
|||
# this script adds all permissions required for full control over the dashboard to
|
||||
# all everybody, who is a member of the admin role
|
||||
|
||||
# Define an array with tuples as strings
|
||||
permissions=(
|
||||
"admin.stack.dashboard#access"
|
||||
"admin.stack.status#access"
|
||||
"admin.user#access"
|
||||
"admin.user#create"
|
||||
"admin.user#edit"
|
||||
"admin.user#delete"
|
||||
"admin.user.session#access"
|
||||
"admin.user.session#delete"
|
||||
"admin.user.state#edit"
|
||||
"admin.user.code#create"
|
||||
"admin.user.link#create"
|
||||
"admin.user.trait#access"
|
||||
"admin.user.trait#edit"
|
||||
"admin.user.address#access"
|
||||
"admin.user.credential#access"
|
||||
"admin.user.credential#delete"
|
||||
)
|
||||
|
||||
# Iterate over the array
|
||||
for permission in "${permissions[@]}"; do
|
||||
|
||||
# split strings
|
||||
IFS='#' read -r OBJECT RELATION <<< "$permission"
|
||||
|
||||
# execute curl to Ory Keto write endpoint
|
||||
curl --silent --request PUT \
|
||||
--url http://localhost:4467/admin/relation-tuples \
|
||||
--data '{
|
||||
"namespace": "permissions",
|
||||
"object": "'"$OBJECT"'",
|
||||
"relation": "'"$RELATION"'",
|
||||
"subject_set": {
|
||||
"namespace": "roles",
|
||||
"object": "admin",
|
||||
"relation": "member"
|
||||
}
|
||||
}' > /dev/null
|
||||
|
||||
# write success response to terminal
|
||||
echo "Added relation Permissions:$OBJECT#$RELATION@(Roles:admin#member)"
|
||||
|
||||
done
|
|
@ -22,6 +22,8 @@ dsn: postgres://postgres:postgres@ory-postgres:5432/keto?sslmode=disable&max_con
|
|||
namespaces:
|
||||
- id: 0
|
||||
name: roles
|
||||
- id: 1
|
||||
name: permissions
|
||||
|
||||
serve:
|
||||
read:
|
||||
|
|
Loading…
Add table
Reference in a new issue