@valentine-efagene/qshelter-common 2.0.87 → 2.0.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/client/browser.d.ts +10 -2
- package/dist/generated/client/client.d.ts +10 -2
- package/dist/generated/client/commonInputTypes.d.ts +30 -0
- package/dist/generated/client/enums.d.ts +5 -0
- package/dist/generated/client/enums.js +4 -0
- package/dist/generated/client/internal/class.d.ts +11 -0
- package/dist/generated/client/internal/class.js +2 -2
- package/dist/generated/client/internal/prismaNamespace.d.ts +113 -16
- package/dist/generated/client/internal/prismaNamespace.js +38 -14
- package/dist/generated/client/internal/prismaNamespaceBrowser.d.ts +41 -15
- package/dist/generated/client/internal/prismaNamespaceBrowser.js +38 -14
- package/dist/generated/client/models/Permission.d.ts +333 -68
- package/dist/generated/client/models/Role.d.ts +403 -3
- package/dist/generated/client/models/Tenant.d.ts +761 -4
- package/dist/generated/client/models/TenantMembership.d.ts +1395 -0
- package/dist/generated/client/models/TenantMembership.js +1 -0
- package/dist/generated/client/models/User.d.ts +375 -0
- package/dist/generated/client/models/UserRole.d.ts +2 -1
- package/dist/generated/client/models/index.d.ts +5 -0
- package/dist/generated/client/models/index.js +5 -0
- package/dist/generated/client/models.d.ts +1 -0
- package/dist/src/events/index.d.ts +2 -0
- package/dist/src/events/index.js +3 -0
- package/dist/src/events/policies/index.d.ts +2 -0
- package/dist/src/events/policies/index.js +2 -0
- package/dist/src/events/policies/policy-event.d.ts +89 -0
- package/dist/src/events/policies/policy-event.js +22 -0
- package/dist/src/events/policies/policy-publisher.d.ts +60 -0
- package/dist/src/events/policies/policy-publisher.js +128 -0
- package/dist/src/prisma/tenant.js +13 -5
- package/package.json +1 -1
- package/prisma/schema.prisma +84 -20
|
@@ -2,7 +2,8 @@ import type * as runtime from "@prisma/client/runtime/client";
|
|
|
2
2
|
import type * as Prisma from "../internal/prismaNamespace.js";
|
|
3
3
|
/**
|
|
4
4
|
* Model UserRole
|
|
5
|
-
*
|
|
5
|
+
* Legacy: Direct user-role assignment (global, not tenant-scoped)
|
|
6
|
+
* @deprecated Use TenantMembership for tenant-scoped role assignments
|
|
6
7
|
*/
|
|
7
8
|
export type UserRoleModel = runtime.Types.Result.DefaultSelection<Prisma.$UserRolePayload>;
|
|
8
9
|
export type AggregateUserRole = {
|
|
@@ -12,6 +12,7 @@ export * from './ContractTermination';
|
|
|
12
12
|
export * from './DeviceEndpoint';
|
|
13
13
|
export * from './DocumentRequirementRule';
|
|
14
14
|
export * from './DocumentTemplate';
|
|
15
|
+
export * from './DocumentationPhase';
|
|
15
16
|
export * from './DocumentationStep';
|
|
16
17
|
export * from './DocumentationStepApproval';
|
|
17
18
|
export * from './DocumentationStepDocument';
|
|
@@ -26,7 +27,9 @@ export * from './OfferLetter';
|
|
|
26
27
|
export * from './PasswordReset';
|
|
27
28
|
export * from './PaymentMethodChangeRequest';
|
|
28
29
|
export * from './PaymentMethodPhaseDocument';
|
|
30
|
+
export * from './PaymentMethodPhaseField';
|
|
29
31
|
export * from './PaymentMethodPhaseStep';
|
|
32
|
+
export * from './PaymentPhase';
|
|
30
33
|
export * from './PaymentPlan';
|
|
31
34
|
export * from './Permission';
|
|
32
35
|
export * from './Property';
|
|
@@ -41,6 +44,8 @@ export * from './PropertyUnit';
|
|
|
41
44
|
export * from './PropertyVariant';
|
|
42
45
|
export * from './PropertyVariantAmenity';
|
|
43
46
|
export * from './PropertyVariantMedia';
|
|
47
|
+
export * from './QuestionnaireField';
|
|
48
|
+
export * from './QuestionnairePhase';
|
|
44
49
|
export * from './RefreshToken';
|
|
45
50
|
export * from './Role';
|
|
46
51
|
export * from './RolePermission';
|
|
@@ -12,6 +12,7 @@ export * from './ContractTermination';
|
|
|
12
12
|
export * from './DeviceEndpoint';
|
|
13
13
|
export * from './DocumentRequirementRule';
|
|
14
14
|
export * from './DocumentTemplate';
|
|
15
|
+
export * from './DocumentationPhase';
|
|
15
16
|
export * from './DocumentationStep';
|
|
16
17
|
export * from './DocumentationStepApproval';
|
|
17
18
|
export * from './DocumentationStepDocument';
|
|
@@ -26,7 +27,9 @@ export * from './OfferLetter';
|
|
|
26
27
|
export * from './PasswordReset';
|
|
27
28
|
export * from './PaymentMethodChangeRequest';
|
|
28
29
|
export * from './PaymentMethodPhaseDocument';
|
|
30
|
+
export * from './PaymentMethodPhaseField';
|
|
29
31
|
export * from './PaymentMethodPhaseStep';
|
|
32
|
+
export * from './PaymentPhase';
|
|
30
33
|
export * from './PaymentPlan';
|
|
31
34
|
export * from './Permission';
|
|
32
35
|
export * from './Property';
|
|
@@ -41,6 +44,8 @@ export * from './PropertyUnit';
|
|
|
41
44
|
export * from './PropertyVariant';
|
|
42
45
|
export * from './PropertyVariantAmenity';
|
|
43
46
|
export * from './PropertyVariantMedia';
|
|
47
|
+
export * from './QuestionnaireField';
|
|
48
|
+
export * from './QuestionnairePhase';
|
|
44
49
|
export * from './RefreshToken';
|
|
45
50
|
export * from './Role';
|
|
46
51
|
export * from './RolePermission';
|
|
@@ -3,6 +3,7 @@ export type * from './models/Role.js';
|
|
|
3
3
|
export type * from './models/Permission.js';
|
|
4
4
|
export type * from './models/RolePermission.js';
|
|
5
5
|
export type * from './models/UserRole.js';
|
|
6
|
+
export type * from './models/TenantMembership.js';
|
|
6
7
|
export type * from './models/Tenant.js';
|
|
7
8
|
export type * from './models/ApiKey.js';
|
|
8
9
|
export type * from './models/RefreshToken.js';
|
|
@@ -3,5 +3,7 @@ export * from './notifications/notification-event';
|
|
|
3
3
|
export * from './notifications/event-publisher';
|
|
4
4
|
export * from './payments/payment-event';
|
|
5
5
|
export * from './payments/payment-publisher';
|
|
6
|
+
export * from './policies/policy-event';
|
|
7
|
+
export * from './policies/policy-publisher';
|
|
6
8
|
export * from './bus/event-bus.types';
|
|
7
9
|
export * from './bus/event-bus.service';
|
package/dist/src/events/index.js
CHANGED
|
@@ -5,6 +5,9 @@ export * from './notifications/event-publisher';
|
|
|
5
5
|
// Payment events and publisher (SNS-based)
|
|
6
6
|
export * from './payments/payment-event';
|
|
7
7
|
export * from './payments/payment-publisher';
|
|
8
|
+
// Policy sync events and publisher (SNS-based)
|
|
9
|
+
export * from './policies/policy-event';
|
|
10
|
+
export * from './policies/policy-publisher';
|
|
8
11
|
// Event bus (multi-transport delivery)
|
|
9
12
|
export * from './bus/event-bus.types';
|
|
10
13
|
export * from './bus/event-bus.service';
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy Sync Event Types
|
|
3
|
+
*
|
|
4
|
+
* These events are published when role/permission data changes in RDS,
|
|
5
|
+
* triggering synchronization to DynamoDB for the authorizer.
|
|
6
|
+
*/
|
|
7
|
+
export declare enum PolicyEventType {
|
|
8
|
+
ROLE_CREATED = "POLICY.ROLE_CREATED",
|
|
9
|
+
ROLE_UPDATED = "POLICY.ROLE_UPDATED",
|
|
10
|
+
ROLE_DELETED = "POLICY.ROLE_DELETED",
|
|
11
|
+
PERMISSION_CREATED = "POLICY.PERMISSION_CREATED",
|
|
12
|
+
PERMISSION_UPDATED = "POLICY.PERMISSION_UPDATED",
|
|
13
|
+
PERMISSION_DELETED = "POLICY.PERMISSION_DELETED",
|
|
14
|
+
ROLE_PERMISSION_ASSIGNED = "POLICY.ROLE_PERMISSION_ASSIGNED",
|
|
15
|
+
ROLE_PERMISSION_REVOKED = "POLICY.ROLE_PERMISSION_REVOKED",
|
|
16
|
+
FULL_SYNC_REQUESTED = "POLICY.FULL_SYNC_REQUESTED"
|
|
17
|
+
}
|
|
18
|
+
export interface RoleData {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
description?: string | null;
|
|
22
|
+
}
|
|
23
|
+
export interface PermissionData {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
description?: string | null;
|
|
27
|
+
resource: string;
|
|
28
|
+
action: string;
|
|
29
|
+
}
|
|
30
|
+
export interface RolePermissionData {
|
|
31
|
+
roleId: string;
|
|
32
|
+
roleName: string;
|
|
33
|
+
permissions: Array<{
|
|
34
|
+
id: string;
|
|
35
|
+
resource: string;
|
|
36
|
+
action: string;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
export interface PolicyEventMeta {
|
|
40
|
+
source: string;
|
|
41
|
+
timestamp: string;
|
|
42
|
+
correlationId?: string;
|
|
43
|
+
userId?: string;
|
|
44
|
+
tenantId?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface PolicyEvent<T = unknown> {
|
|
47
|
+
eventType: PolicyEventType;
|
|
48
|
+
eventId: string;
|
|
49
|
+
timestamp: string;
|
|
50
|
+
tenantId?: string;
|
|
51
|
+
source: string;
|
|
52
|
+
data: T;
|
|
53
|
+
metadata?: Record<string, unknown>;
|
|
54
|
+
}
|
|
55
|
+
export interface RoleCreatedEvent extends PolicyEvent<RoleData> {
|
|
56
|
+
eventType: PolicyEventType.ROLE_CREATED;
|
|
57
|
+
}
|
|
58
|
+
export interface RoleUpdatedEvent extends PolicyEvent<RoleData> {
|
|
59
|
+
eventType: PolicyEventType.ROLE_UPDATED;
|
|
60
|
+
}
|
|
61
|
+
export interface RoleDeletedEvent extends PolicyEvent<{
|
|
62
|
+
roleId: string;
|
|
63
|
+
roleName: string;
|
|
64
|
+
}> {
|
|
65
|
+
eventType: PolicyEventType.ROLE_DELETED;
|
|
66
|
+
}
|
|
67
|
+
export interface PermissionCreatedEvent extends PolicyEvent<PermissionData> {
|
|
68
|
+
eventType: PolicyEventType.PERMISSION_CREATED;
|
|
69
|
+
}
|
|
70
|
+
export interface PermissionUpdatedEvent extends PolicyEvent<PermissionData> {
|
|
71
|
+
eventType: PolicyEventType.PERMISSION_UPDATED;
|
|
72
|
+
}
|
|
73
|
+
export interface PermissionDeletedEvent extends PolicyEvent<{
|
|
74
|
+
permissionId: string;
|
|
75
|
+
}> {
|
|
76
|
+
eventType: PolicyEventType.PERMISSION_DELETED;
|
|
77
|
+
}
|
|
78
|
+
export interface RolePermissionAssignedEvent extends PolicyEvent<RolePermissionData> {
|
|
79
|
+
eventType: PolicyEventType.ROLE_PERMISSION_ASSIGNED;
|
|
80
|
+
}
|
|
81
|
+
export interface RolePermissionRevokedEvent extends PolicyEvent<RolePermissionData> {
|
|
82
|
+
eventType: PolicyEventType.ROLE_PERMISSION_REVOKED;
|
|
83
|
+
}
|
|
84
|
+
export interface FullSyncRequestedEvent extends PolicyEvent<{
|
|
85
|
+
requestedBy: string;
|
|
86
|
+
}> {
|
|
87
|
+
eventType: PolicyEventType.FULL_SYNC_REQUESTED;
|
|
88
|
+
}
|
|
89
|
+
export type AnyPolicyEvent = RoleCreatedEvent | RoleUpdatedEvent | RoleDeletedEvent | PermissionCreatedEvent | PermissionUpdatedEvent | PermissionDeletedEvent | RolePermissionAssignedEvent | RolePermissionRevokedEvent | FullSyncRequestedEvent;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy Sync Event Types
|
|
3
|
+
*
|
|
4
|
+
* These events are published when role/permission data changes in RDS,
|
|
5
|
+
* triggering synchronization to DynamoDB for the authorizer.
|
|
6
|
+
*/
|
|
7
|
+
export var PolicyEventType;
|
|
8
|
+
(function (PolicyEventType) {
|
|
9
|
+
// Role events
|
|
10
|
+
PolicyEventType["ROLE_CREATED"] = "POLICY.ROLE_CREATED";
|
|
11
|
+
PolicyEventType["ROLE_UPDATED"] = "POLICY.ROLE_UPDATED";
|
|
12
|
+
PolicyEventType["ROLE_DELETED"] = "POLICY.ROLE_DELETED";
|
|
13
|
+
// Permission events
|
|
14
|
+
PolicyEventType["PERMISSION_CREATED"] = "POLICY.PERMISSION_CREATED";
|
|
15
|
+
PolicyEventType["PERMISSION_UPDATED"] = "POLICY.PERMISSION_UPDATED";
|
|
16
|
+
PolicyEventType["PERMISSION_DELETED"] = "POLICY.PERMISSION_DELETED";
|
|
17
|
+
// Role-Permission association events
|
|
18
|
+
PolicyEventType["ROLE_PERMISSION_ASSIGNED"] = "POLICY.ROLE_PERMISSION_ASSIGNED";
|
|
19
|
+
PolicyEventType["ROLE_PERMISSION_REVOKED"] = "POLICY.ROLE_PERMISSION_REVOKED";
|
|
20
|
+
// Bulk sync events
|
|
21
|
+
PolicyEventType["FULL_SYNC_REQUESTED"] = "POLICY.FULL_SYNC_REQUESTED";
|
|
22
|
+
})(PolicyEventType || (PolicyEventType = {}));
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { PolicyEventType, PolicyEventMeta, RoleData, PermissionData, RolePermissionData } from './policy-event';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for the policy event publisher
|
|
4
|
+
*/
|
|
5
|
+
interface PolicyPublisherConfig {
|
|
6
|
+
region?: string;
|
|
7
|
+
endpoint?: string;
|
|
8
|
+
topicArn?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Policy Event Publisher for sending policy sync events to SNS
|
|
12
|
+
* Used by user-service to notify policy-sync-service of changes
|
|
13
|
+
*/
|
|
14
|
+
export declare class PolicyEventPublisher {
|
|
15
|
+
private readonly snsClient;
|
|
16
|
+
private readonly topicArn;
|
|
17
|
+
private readonly serviceName;
|
|
18
|
+
constructor(serviceName: string, config?: PolicyPublisherConfig);
|
|
19
|
+
/**
|
|
20
|
+
* Publish a policy event to SNS
|
|
21
|
+
*/
|
|
22
|
+
publish<T>(eventType: PolicyEventType, data: T, meta?: Partial<PolicyEventMeta>): Promise<string>;
|
|
23
|
+
/**
|
|
24
|
+
* Publish role created event
|
|
25
|
+
*/
|
|
26
|
+
publishRoleCreated(role: RoleData, meta?: Partial<PolicyEventMeta>): Promise<string>;
|
|
27
|
+
/**
|
|
28
|
+
* Publish role updated event
|
|
29
|
+
*/
|
|
30
|
+
publishRoleUpdated(role: RoleData, meta?: Partial<PolicyEventMeta>): Promise<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Publish role deleted event
|
|
33
|
+
*/
|
|
34
|
+
publishRoleDeleted(roleId: string, roleName: string, meta?: Partial<PolicyEventMeta>): Promise<string>;
|
|
35
|
+
/**
|
|
36
|
+
* Publish permission created event
|
|
37
|
+
*/
|
|
38
|
+
publishPermissionCreated(permission: PermissionData, meta?: Partial<PolicyEventMeta>): Promise<string>;
|
|
39
|
+
/**
|
|
40
|
+
* Publish permission updated event
|
|
41
|
+
*/
|
|
42
|
+
publishPermissionUpdated(permission: PermissionData, meta?: Partial<PolicyEventMeta>): Promise<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Publish permission deleted event
|
|
45
|
+
*/
|
|
46
|
+
publishPermissionDeleted(permissionId: string, meta?: Partial<PolicyEventMeta>): Promise<string>;
|
|
47
|
+
/**
|
|
48
|
+
* Publish role permission assigned event
|
|
49
|
+
*/
|
|
50
|
+
publishRolePermissionAssigned(data: RolePermissionData, meta?: Partial<PolicyEventMeta>): Promise<string>;
|
|
51
|
+
/**
|
|
52
|
+
* Publish role permission revoked event
|
|
53
|
+
*/
|
|
54
|
+
publishRolePermissionRevoked(data: RolePermissionData, meta?: Partial<PolicyEventMeta>): Promise<string>;
|
|
55
|
+
/**
|
|
56
|
+
* Publish full sync requested event
|
|
57
|
+
*/
|
|
58
|
+
publishFullSyncRequested(requestedBy: string, meta?: Partial<PolicyEventMeta>): Promise<string>;
|
|
59
|
+
}
|
|
60
|
+
export {};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { SNSClient, PublishCommand } from '@aws-sdk/client-sns';
|
|
2
|
+
import { PolicyEventType, } from './policy-event';
|
|
3
|
+
/**
|
|
4
|
+
* Get SNS client configured for LocalStack or AWS
|
|
5
|
+
*/
|
|
6
|
+
function createSNSClient(config) {
|
|
7
|
+
const endpoint = config.endpoint || process.env.LOCALSTACK_ENDPOINT;
|
|
8
|
+
const region = config.region || process.env.AWS_REGION || 'us-east-1';
|
|
9
|
+
const clientConfig = { region };
|
|
10
|
+
// For LocalStack, set custom endpoint
|
|
11
|
+
if (endpoint) {
|
|
12
|
+
clientConfig.endpoint = endpoint;
|
|
13
|
+
}
|
|
14
|
+
return new SNSClient(clientConfig);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Policy Event Publisher for sending policy sync events to SNS
|
|
18
|
+
* Used by user-service to notify policy-sync-service of changes
|
|
19
|
+
*/
|
|
20
|
+
export class PolicyEventPublisher {
|
|
21
|
+
snsClient;
|
|
22
|
+
topicArn;
|
|
23
|
+
serviceName;
|
|
24
|
+
constructor(serviceName, config) {
|
|
25
|
+
this.serviceName = serviceName;
|
|
26
|
+
this.snsClient = createSNSClient(config || {});
|
|
27
|
+
// Topic ARN can be passed directly or constructed from env vars
|
|
28
|
+
const stage = process.env.STAGE || process.env.NODE_ENV || 'test';
|
|
29
|
+
const region = config?.region || process.env.AWS_REGION || 'us-east-1';
|
|
30
|
+
const accountId = process.env.AWS_ACCOUNT_ID || '000000000000';
|
|
31
|
+
this.topicArn = config?.topicArn ||
|
|
32
|
+
process.env.POLICY_SYNC_TOPIC_ARN ||
|
|
33
|
+
`arn:aws:sns:${region}:${accountId}:qshelter-${stage}-policy-sync`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Publish a policy event to SNS
|
|
37
|
+
*/
|
|
38
|
+
async publish(eventType, data, meta) {
|
|
39
|
+
const eventId = crypto.randomUUID();
|
|
40
|
+
const event = {
|
|
41
|
+
eventType,
|
|
42
|
+
eventId,
|
|
43
|
+
timestamp: new Date().toISOString(),
|
|
44
|
+
source: this.serviceName,
|
|
45
|
+
tenantId: meta?.tenantId,
|
|
46
|
+
data,
|
|
47
|
+
metadata: {
|
|
48
|
+
correlationId: meta?.correlationId || eventId,
|
|
49
|
+
userId: meta?.userId,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
const command = new PublishCommand({
|
|
53
|
+
TopicArn: this.topicArn,
|
|
54
|
+
Message: JSON.stringify(event),
|
|
55
|
+
MessageAttributes: {
|
|
56
|
+
eventType: {
|
|
57
|
+
DataType: 'String',
|
|
58
|
+
StringValue: eventType,
|
|
59
|
+
},
|
|
60
|
+
source: {
|
|
61
|
+
DataType: 'String',
|
|
62
|
+
StringValue: this.serviceName,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
const result = await this.snsClient.send(command);
|
|
67
|
+
console.log(`[PolicyEventPublisher] Published ${eventType} event to SNS`, {
|
|
68
|
+
topicArn: this.topicArn,
|
|
69
|
+
messageId: result.MessageId,
|
|
70
|
+
eventId,
|
|
71
|
+
});
|
|
72
|
+
return result.MessageId || '';
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Publish role created event
|
|
76
|
+
*/
|
|
77
|
+
async publishRoleCreated(role, meta) {
|
|
78
|
+
return this.publish(PolicyEventType.ROLE_CREATED, role, meta);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Publish role updated event
|
|
82
|
+
*/
|
|
83
|
+
async publishRoleUpdated(role, meta) {
|
|
84
|
+
return this.publish(PolicyEventType.ROLE_UPDATED, role, meta);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Publish role deleted event
|
|
88
|
+
*/
|
|
89
|
+
async publishRoleDeleted(roleId, roleName, meta) {
|
|
90
|
+
return this.publish(PolicyEventType.ROLE_DELETED, { roleId, roleName }, meta);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Publish permission created event
|
|
94
|
+
*/
|
|
95
|
+
async publishPermissionCreated(permission, meta) {
|
|
96
|
+
return this.publish(PolicyEventType.PERMISSION_CREATED, permission, meta);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Publish permission updated event
|
|
100
|
+
*/
|
|
101
|
+
async publishPermissionUpdated(permission, meta) {
|
|
102
|
+
return this.publish(PolicyEventType.PERMISSION_UPDATED, permission, meta);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Publish permission deleted event
|
|
106
|
+
*/
|
|
107
|
+
async publishPermissionDeleted(permissionId, meta) {
|
|
108
|
+
return this.publish(PolicyEventType.PERMISSION_DELETED, { permissionId }, meta);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Publish role permission assigned event
|
|
112
|
+
*/
|
|
113
|
+
async publishRolePermissionAssigned(data, meta) {
|
|
114
|
+
return this.publish(PolicyEventType.ROLE_PERMISSION_ASSIGNED, data, meta);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Publish role permission revoked event
|
|
118
|
+
*/
|
|
119
|
+
async publishRolePermissionRevoked(data, meta) {
|
|
120
|
+
return this.publish(PolicyEventType.ROLE_PERMISSION_REVOKED, data, meta);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Publish full sync requested event
|
|
124
|
+
*/
|
|
125
|
+
async publishFullSyncRequested(requestedBy, meta) {
|
|
126
|
+
return this.publish(PolicyEventType.FULL_SYNC_REQUESTED, { requestedBy }, meta);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -9,16 +9,18 @@
|
|
|
9
9
|
* These models either:
|
|
10
10
|
* - Don't have a tenantId field (system tables)
|
|
11
11
|
* - Have optional tenantId but are designed to work across tenants (User)
|
|
12
|
+
* - Are cross-tenant lookup/join tables (TenantMembership)
|
|
12
13
|
*/
|
|
13
14
|
const GLOBAL_MODELS = [
|
|
14
|
-
// User can exist across tenants or without a tenant
|
|
15
|
+
// User can exist across tenants or without a tenant (federated)
|
|
15
16
|
"user",
|
|
17
|
+
// TenantMembership is the user-tenant join table (queries by userId or tenantId)
|
|
18
|
+
"tenantMembership",
|
|
16
19
|
// System/infrastructure tables without tenantId
|
|
17
20
|
"tenant",
|
|
18
|
-
|
|
19
|
-
"permission",
|
|
20
|
-
"rolePermission",
|
|
21
|
+
// Legacy role assignment (global, not tenant-scoped)
|
|
21
22
|
"userRole",
|
|
23
|
+
"rolePermission",
|
|
22
24
|
"refreshToken",
|
|
23
25
|
"passwordReset",
|
|
24
26
|
"wallet",
|
|
@@ -29,7 +31,13 @@ const GLOBAL_MODELS = [
|
|
|
29
31
|
* These can be global templates (tenantId = null) or tenant-specific.
|
|
30
32
|
* Queries will return both global AND tenant-specific records.
|
|
31
33
|
*/
|
|
32
|
-
const OPTIONAL_TENANT_MODELS = [
|
|
34
|
+
const OPTIONAL_TENANT_MODELS = [
|
|
35
|
+
"paymentPlan",
|
|
36
|
+
// Role can be global template (tenantId = null) or tenant-specific
|
|
37
|
+
"role",
|
|
38
|
+
// Permission can be global template or tenant-specific
|
|
39
|
+
"permission",
|
|
40
|
+
];
|
|
33
41
|
function isGlobalModel(model) {
|
|
34
42
|
return GLOBAL_MODELS.includes(model);
|
|
35
43
|
}
|
package/package.json
CHANGED
package/prisma/schema.prisma
CHANGED
|
@@ -292,29 +292,39 @@ enum ExecutionStatus {
|
|
|
292
292
|
SKIPPED
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
+
/// Permission effect (Allow/Deny)
|
|
296
|
+
enum PermissionEffect {
|
|
297
|
+
ALLOW
|
|
298
|
+
DENY
|
|
299
|
+
}
|
|
300
|
+
|
|
295
301
|
// =============================================================================
|
|
296
302
|
// USER & AUTH DOMAIN
|
|
297
303
|
// =============================================================================
|
|
298
304
|
|
|
299
305
|
model User {
|
|
300
|
-
id String
|
|
301
|
-
email String
|
|
306
|
+
id String @id @default(cuid())
|
|
307
|
+
email String @unique
|
|
302
308
|
password String?
|
|
303
|
-
phone String?
|
|
309
|
+
phone String? @unique
|
|
304
310
|
firstName String?
|
|
305
311
|
lastName String?
|
|
306
|
-
isActive Boolean
|
|
307
|
-
isEmailVerified Boolean
|
|
312
|
+
isActive Boolean @default(true)
|
|
313
|
+
isEmailVerified Boolean @default(false)
|
|
308
314
|
googleId String?
|
|
309
315
|
avatar String?
|
|
316
|
+
// Legacy: Optional direct tenant association (for backward compatibility)
|
|
317
|
+
// New: Use tenantMemberships for multi-tenant federation
|
|
310
318
|
tenantId String?
|
|
311
|
-
tenant Tenant?
|
|
312
|
-
//
|
|
319
|
+
tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: SetNull)
|
|
320
|
+
// Federated: User can belong to multiple tenants with different roles
|
|
321
|
+
tenantMemberships TenantMembership[]
|
|
322
|
+
// Legacy: Support multiple roles via explicit join table `UserRole`
|
|
313
323
|
userRoles UserRole[]
|
|
314
|
-
walletId String?
|
|
315
|
-
wallet Wallet?
|
|
316
|
-
createdAt DateTime
|
|
317
|
-
updatedAt DateTime
|
|
324
|
+
walletId String? @unique
|
|
325
|
+
wallet Wallet? @relation(fields: [walletId], references: [id])
|
|
326
|
+
createdAt DateTime @default(now())
|
|
327
|
+
updatedAt DateTime @updatedAt
|
|
318
328
|
emailVerifiedAt DateTime?
|
|
319
329
|
emailVerificationToken String?
|
|
320
330
|
lastLoginAt DateTime?
|
|
@@ -368,29 +378,51 @@ model User {
|
|
|
368
378
|
}
|
|
369
379
|
|
|
370
380
|
model Role {
|
|
371
|
-
id String
|
|
372
|
-
name String
|
|
381
|
+
id String @id @default(cuid())
|
|
382
|
+
name String
|
|
373
383
|
description String?
|
|
384
|
+
// Tenant-scoping: NULL = global template, set = tenant-specific role
|
|
385
|
+
tenantId String?
|
|
386
|
+
tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
387
|
+
// System roles cannot be deleted (admin, user, etc.)
|
|
388
|
+
isSystem Boolean @default(false)
|
|
389
|
+
isActive Boolean @default(true)
|
|
390
|
+
// Legacy: UserRole for backward compatibility
|
|
374
391
|
userRoles UserRole[]
|
|
392
|
+
// New: TenantMembership for federated users
|
|
393
|
+
memberships TenantMembership[]
|
|
375
394
|
permissions RolePermission[]
|
|
376
|
-
createdAt DateTime
|
|
377
|
-
updatedAt DateTime
|
|
395
|
+
createdAt DateTime @default(now())
|
|
396
|
+
updatedAt DateTime @updatedAt
|
|
378
397
|
|
|
398
|
+
@@unique([name, tenantId]) // Unique name per tenant (null tenantId = global)
|
|
399
|
+
@@index([tenantId])
|
|
379
400
|
@@map("roles")
|
|
380
401
|
}
|
|
381
402
|
|
|
403
|
+
/// Permission defines a path pattern + HTTP methods + effect
|
|
404
|
+
/// Supports path-based authorization matching the authorizer's policy structure
|
|
382
405
|
model Permission {
|
|
383
406
|
id String @id @default(cuid())
|
|
384
|
-
name String
|
|
407
|
+
name String // Descriptive name: "Read Users", "Manage Properties"
|
|
385
408
|
description String?
|
|
386
|
-
|
|
387
|
-
|
|
409
|
+
// Path pattern: /users, /users/:id, /properties/*, etc.
|
|
410
|
+
path String
|
|
411
|
+
// HTTP methods: ["GET"], ["GET", "POST"], ["*"] - stored as JSON
|
|
412
|
+
methods Json @default("[]")
|
|
413
|
+
// Allow or Deny this path/methods
|
|
414
|
+
effect PermissionEffect @default(ALLOW)
|
|
415
|
+
// Tenant-scoping: NULL = global template, set = tenant-specific
|
|
416
|
+
tenantId String?
|
|
417
|
+
tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
418
|
+
// System permissions cannot be deleted
|
|
419
|
+
isSystem Boolean @default(false)
|
|
388
420
|
roles RolePermission[]
|
|
389
421
|
createdAt DateTime @default(now())
|
|
390
422
|
updatedAt DateTime @updatedAt
|
|
391
423
|
|
|
392
|
-
@@unique([
|
|
393
|
-
@@index([
|
|
424
|
+
@@unique([path, tenantId]) // Unique path per tenant
|
|
425
|
+
@@index([tenantId])
|
|
394
426
|
@@map("permissions")
|
|
395
427
|
}
|
|
396
428
|
|
|
@@ -405,6 +437,8 @@ model RolePermission {
|
|
|
405
437
|
@@map("role_permissions")
|
|
406
438
|
}
|
|
407
439
|
|
|
440
|
+
/// Legacy: Direct user-role assignment (global, not tenant-scoped)
|
|
441
|
+
/// @deprecated Use TenantMembership for tenant-scoped role assignments
|
|
408
442
|
model UserRole {
|
|
409
443
|
userId String
|
|
410
444
|
roleId String
|
|
@@ -416,6 +450,30 @@ model UserRole {
|
|
|
416
450
|
@@map("user_roles")
|
|
417
451
|
}
|
|
418
452
|
|
|
453
|
+
/// Tenant Membership: Links users to tenants with specific roles
|
|
454
|
+
/// Enables federated users across multiple tenants with different roles per tenant
|
|
455
|
+
model TenantMembership {
|
|
456
|
+
id String @id @default(cuid())
|
|
457
|
+
userId String
|
|
458
|
+
tenantId String
|
|
459
|
+
roleId String
|
|
460
|
+
// Whether this membership is active
|
|
461
|
+
isActive Boolean @default(true)
|
|
462
|
+
// Whether this is the user's default tenant (for login without specifying tenant)
|
|
463
|
+
isDefault Boolean @default(false)
|
|
464
|
+
createdAt DateTime @default(now())
|
|
465
|
+
updatedAt DateTime @updatedAt
|
|
466
|
+
|
|
467
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
468
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
469
|
+
role Role @relation(fields: [roleId], references: [id], onDelete: Restrict)
|
|
470
|
+
|
|
471
|
+
@@unique([userId, tenantId]) // User can only have one membership per tenant
|
|
472
|
+
@@index([tenantId])
|
|
473
|
+
@@index([userId])
|
|
474
|
+
@@map("tenant_memberships")
|
|
475
|
+
}
|
|
476
|
+
|
|
419
477
|
model Tenant {
|
|
420
478
|
id String @id @default(cuid())
|
|
421
479
|
name String
|
|
@@ -431,6 +489,12 @@ model Tenant {
|
|
|
431
489
|
paymentMethods PropertyPaymentMethod[]
|
|
432
490
|
contracts Contract[]
|
|
433
491
|
|
|
492
|
+
// RBAC: Tenant-scoped roles and permissions
|
|
493
|
+
roles Role[]
|
|
494
|
+
permissions Permission[]
|
|
495
|
+
// Federated user memberships
|
|
496
|
+
memberships TenantMembership[]
|
|
497
|
+
|
|
434
498
|
// Payment method changes
|
|
435
499
|
paymentMethodChangeRequests PaymentMethodChangeRequest[]
|
|
436
500
|
documentRequirementRules DocumentRequirementRule[]
|