@rhino-dev/rhino-nestjs 4.0.0 → 4.2.1
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/README.md +52 -1
- package/dist/blueprint/blueprint-runner.js +32 -1
- package/dist/blueprint/blueprint-runner.js.map +1 -1
- package/dist/blueprint/blueprint-sorter.d.ts +54 -0
- package/dist/blueprint/blueprint-sorter.js +107 -0
- package/dist/blueprint/blueprint-sorter.js.map +1 -0
- package/dist/controllers/auth.controller.d.ts +34 -5
- package/dist/controllers/auth.controller.js +132 -12
- package/dist/controllers/auth.controller.js.map +1 -1
- package/dist/controllers/invitation.controller.d.ts +41 -2
- package/dist/controllers/invitation.controller.js +97 -12
- package/dist/controllers/invitation.controller.js.map +1 -1
- package/dist/errors/rhino-exception.d.ts +12 -1
- package/dist/errors/rhino-exception.js +19 -1
- package/dist/errors/rhino-exception.js.map +1 -1
- package/dist/guards/group-membership.guard.d.ts +25 -0
- package/dist/guards/group-membership.guard.js +70 -0
- package/dist/guards/group-membership.guard.js.map +1 -0
- package/dist/guards/jwt-auth.guard.js +23 -4
- package/dist/guards/jwt-auth.guard.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +17 -2
- package/dist/index.js.map +1 -1
- package/dist/interfaces/rhino-config.interface.d.ts +71 -0
- package/dist/middleware/domain-route-resolver.d.ts +54 -0
- package/dist/middleware/domain-route-resolver.js +145 -0
- package/dist/middleware/domain-route-resolver.js.map +1 -0
- package/dist/middleware/route-group.middleware.d.ts +48 -4
- package/dist/middleware/route-group.middleware.js +274 -13
- package/dist/middleware/route-group.middleware.js.map +1 -1
- package/dist/rhino.config.d.ts +26 -1
- package/dist/rhino.config.js +56 -1
- package/dist/rhino.config.js.map +1 -1
- package/dist/rhino.module.d.ts +7 -0
- package/dist/rhino.module.js +23 -1
- package/dist/rhino.module.js.map +1 -1
- package/dist/services/auth-hooks.service.d.ts +27 -0
- package/dist/services/auth-hooks.service.js +85 -0
- package/dist/services/auth-hooks.service.js.map +1 -0
- package/dist/services/auth.service.d.ts +19 -0
- package/dist/services/auth.service.js +80 -5
- package/dist/services/auth.service.js.map +1 -1
- package/dist/services/invitation.service.d.ts +6 -0
- package/dist/services/invitation.service.js +45 -3
- package/dist/services/invitation.service.js.map +1 -1
- package/dist/services/membership.service.d.ts +56 -0
- package/dist/services/membership.service.js +113 -0
- package/dist/services/membership.service.js.map +1 -0
- package/dist/services/organization.service.js +8 -1
- package/dist/services/organization.service.js.map +1 -1
- package/dist/services/route-registration.service.d.ts +1 -0
- package/dist/services/route-registration.service.js +2 -0
- package/dist/services/route-registration.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/domain-pattern.d.ts +36 -0
- package/dist/utils/domain-pattern.js +77 -0
- package/dist/utils/domain-pattern.js.map +1 -0
- package/dist/utils/permission-matcher.js +8 -0
- package/dist/utils/permission-matcher.js.map +1 -1
- package/dist/utils/route-group-validator.d.ts +14 -0
- package/dist/utils/route-group-validator.js +124 -0
- package/dist/utils/route-group-validator.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,15 +1,52 @@
|
|
|
1
1
|
import { InvitationService } from '../services/invitation.service';
|
|
2
|
+
import { RhinoConfigService } from '../rhino.config';
|
|
3
|
+
import { MembershipService } from '../services/membership.service';
|
|
2
4
|
export declare class InvitationController {
|
|
3
5
|
private readonly invitations;
|
|
4
|
-
|
|
5
|
-
private
|
|
6
|
+
private readonly config;
|
|
7
|
+
private readonly membership;
|
|
8
|
+
constructor(invitations: InvitationService, config: RhinoConfigService, membership: MembershipService);
|
|
9
|
+
/**
|
|
10
|
+
* Authorize an invitation action.
|
|
11
|
+
*
|
|
12
|
+
* - Enforcement OFF (legacy): byte-for-byte the original check —
|
|
13
|
+
* `userHasPermission(user, invitations.{action}, org)`; 403 on failure.
|
|
14
|
+
* - Enforcement ON: a COARSE membership gate runs FIRST — the inviter must be
|
|
15
|
+
* a member of the target group (design §8 — "inviter must be a member of the
|
|
16
|
+
* group"). A non-member is a **403** for parity with Laravel/Rails
|
|
17
|
+
* (Decision 9.C), NOT a 400. A NULL/absent membership row is a wildcard and
|
|
18
|
+
* admits the inviter. The fine-grained permission check then runs (also 403
|
|
19
|
+
* on failure), resolving via the established heuristic (tenant groups → the
|
|
20
|
+
* org's user_roles permissions, non-tenant groups → top-level
|
|
21
|
+
* user.permissions). The two checks run in sequence and are never merged.
|
|
22
|
+
*/
|
|
23
|
+
private assertCanInvite;
|
|
24
|
+
/** The `public` group (reserved name or any `skipAuth` group) has no auth, so it cannot be invited into. */
|
|
25
|
+
private isPublicGroup;
|
|
26
|
+
/**
|
|
27
|
+
* Reject a `routeGroup` that is neither a configured route group nor the
|
|
28
|
+
* reserved `public` name. A client could otherwise forge an arbitrary group
|
|
29
|
+
* value (overriding the resolved group) and create a dormant, unaudited grant
|
|
30
|
+
* that activates the day that group name is wired up. This guard runs
|
|
31
|
+
* regardless of enforcement. NULL (wildcard) is always allowed.
|
|
32
|
+
*/
|
|
33
|
+
private assertKnownGroup;
|
|
6
34
|
index(req: any, status?: string): Promise<{
|
|
7
35
|
data: any;
|
|
8
36
|
}>;
|
|
9
37
|
store(req: any, body: {
|
|
10
38
|
email: string;
|
|
11
39
|
roleId: number;
|
|
40
|
+
routeGroup?: string | null;
|
|
12
41
|
}): Promise<any>;
|
|
42
|
+
/**
|
|
43
|
+
* Whether this invite requires an organization context. We default to YES to
|
|
44
|
+
* preserve legacy behavior (store always required an org). An invite is only
|
|
45
|
+
* treated as non-tenant — and thus org-optional — when membership enforcement
|
|
46
|
+
* is on AND the resolved group is positively a non-tenant group. This keeps
|
|
47
|
+
* every flag-off code path byte-for-byte unchanged.
|
|
48
|
+
*/
|
|
49
|
+
private isTenantInvite;
|
|
13
50
|
resend(req: any, id: string): Promise<any>;
|
|
14
51
|
cancel(req: any, id: string): Promise<any>;
|
|
15
52
|
accept(body: {
|
|
@@ -19,10 +56,12 @@ export declare class InvitationController {
|
|
|
19
56
|
organization: any;
|
|
20
57
|
role: any;
|
|
21
58
|
email: any;
|
|
59
|
+
routeGroup: any;
|
|
22
60
|
accepted?: undefined;
|
|
23
61
|
} | {
|
|
24
62
|
accepted: boolean;
|
|
25
63
|
organization: any;
|
|
64
|
+
routeGroup: any;
|
|
26
65
|
requiresRegistration?: undefined;
|
|
27
66
|
role?: undefined;
|
|
28
67
|
email?: undefined;
|
|
@@ -15,46 +15,129 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.InvitationController = void 0;
|
|
16
16
|
const common_1 = require("@nestjs/common");
|
|
17
17
|
const invitation_service_1 = require("../services/invitation.service");
|
|
18
|
+
const rhino_config_1 = require("../rhino.config");
|
|
19
|
+
const membership_service_1 = require("../services/membership.service");
|
|
18
20
|
const permission_matcher_1 = require("../utils/permission-matcher");
|
|
21
|
+
const rhino_exception_1 = require("../errors/rhino-exception");
|
|
19
22
|
let InvitationController = class InvitationController {
|
|
20
23
|
invitations;
|
|
21
|
-
|
|
24
|
+
config;
|
|
25
|
+
membership;
|
|
26
|
+
constructor(invitations, config, membership) {
|
|
22
27
|
this.invitations = invitations;
|
|
28
|
+
this.config = config;
|
|
29
|
+
this.membership = membership;
|
|
23
30
|
}
|
|
24
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Authorize an invitation action.
|
|
33
|
+
*
|
|
34
|
+
* - Enforcement OFF (legacy): byte-for-byte the original check —
|
|
35
|
+
* `userHasPermission(user, invitations.{action}, org)`; 403 on failure.
|
|
36
|
+
* - Enforcement ON: a COARSE membership gate runs FIRST — the inviter must be
|
|
37
|
+
* a member of the target group (design §8 — "inviter must be a member of the
|
|
38
|
+
* group"). A non-member is a **403** for parity with Laravel/Rails
|
|
39
|
+
* (Decision 9.C), NOT a 400. A NULL/absent membership row is a wildcard and
|
|
40
|
+
* admits the inviter. The fine-grained permission check then runs (also 403
|
|
41
|
+
* on failure), resolving via the established heuristic (tenant groups → the
|
|
42
|
+
* org's user_roles permissions, non-tenant groups → top-level
|
|
43
|
+
* user.permissions). The two checks run in sequence and are never merged.
|
|
44
|
+
*/
|
|
45
|
+
assertCanInvite(req, routeGroup, action) {
|
|
46
|
+
if (this.membership.enabled()) {
|
|
47
|
+
const isTenant = this.config.isTenantGroup(routeGroup ?? undefined);
|
|
48
|
+
if (!this.membership.isMember(req.user, routeGroup ?? null, req.organization, isTenant)) {
|
|
49
|
+
throw rhino_exception_1.RhinoException.membershipDenied(`Inviter is not a member of group '${routeGroup ?? '(any)'}'`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
25
52
|
if (!(0, permission_matcher_1.userHasPermission)(req.user, `invitations.${action}`, req.organization)) {
|
|
26
|
-
|
|
27
|
-
err.status = 403;
|
|
28
|
-
throw err;
|
|
53
|
+
throw rhino_exception_1.RhinoException.forbidden();
|
|
29
54
|
}
|
|
30
55
|
}
|
|
56
|
+
/** The `public` group (reserved name or any `skipAuth` group) has no auth, so it cannot be invited into. */
|
|
57
|
+
isPublicGroup(routeGroup) {
|
|
58
|
+
if (routeGroup == null)
|
|
59
|
+
return false;
|
|
60
|
+
if (routeGroup === 'public')
|
|
61
|
+
return true;
|
|
62
|
+
return this.config.routeGroups()?.[routeGroup]?.skipAuth === true;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Reject a `routeGroup` that is neither a configured route group nor the
|
|
66
|
+
* reserved `public` name. A client could otherwise forge an arbitrary group
|
|
67
|
+
* value (overriding the resolved group) and create a dormant, unaudited grant
|
|
68
|
+
* that activates the day that group name is wired up. This guard runs
|
|
69
|
+
* regardless of enforcement. NULL (wildcard) is always allowed.
|
|
70
|
+
*/
|
|
71
|
+
assertKnownGroup(routeGroup) {
|
|
72
|
+
if (routeGroup == null)
|
|
73
|
+
return; // NULL wildcard
|
|
74
|
+
if (routeGroup === 'public')
|
|
75
|
+
return; // reserved; rejected later by isPublicGroup
|
|
76
|
+
if (this.config.routeGroups()?.[routeGroup])
|
|
77
|
+
return; // a configured group
|
|
78
|
+
throw rhino_exception_1.RhinoException.validationFailed({
|
|
79
|
+
routeGroup: [`Unknown route group '${routeGroup}'`],
|
|
80
|
+
});
|
|
81
|
+
}
|
|
31
82
|
async index(req, status) {
|
|
32
|
-
this.
|
|
83
|
+
this.assertCanInvite(req, req.__routeGroup ?? null, 'index');
|
|
33
84
|
if (!req.organization)
|
|
34
85
|
throw new common_1.BadRequestException('Organization context required');
|
|
35
86
|
const items = await this.invitations.list(req.organization.id, status);
|
|
36
87
|
return { data: items };
|
|
37
88
|
}
|
|
38
89
|
async store(req, body) {
|
|
39
|
-
this.assertInvitationPermission(req, 'store');
|
|
40
|
-
if (!req.organization)
|
|
41
|
-
throw new common_1.BadRequestException('Organization context required');
|
|
42
90
|
if (!body?.email || !body?.roleId) {
|
|
43
91
|
throw new common_1.BadRequestException('email and roleId required');
|
|
44
92
|
}
|
|
93
|
+
// The invite's group: an explicit body value, else the request's resolved
|
|
94
|
+
// group (set by RouteGroupMiddleware), else NULL (wildcard).
|
|
95
|
+
const routeGroup = body.routeGroup !== undefined
|
|
96
|
+
? body.routeGroup
|
|
97
|
+
: req.__routeGroup ?? null;
|
|
98
|
+
// Reject a forged/unknown route group (422) before any other processing so
|
|
99
|
+
// a client can never seed a dormant grant for an unconfigured group.
|
|
100
|
+
this.assertKnownGroup(routeGroup);
|
|
101
|
+
// The public group has no auth and cannot be invited into.
|
|
102
|
+
if (this.isPublicGroup(routeGroup)) {
|
|
103
|
+
throw new common_1.BadRequestException('Cannot invite into the public group');
|
|
104
|
+
}
|
|
105
|
+
// Tenant-group invites still require an org context; non-tenant invites
|
|
106
|
+
// (an explicit NULL/non-tenant group) may proceed without one. This is
|
|
107
|
+
// checked BEFORE authorization so the legacy "org required" 400 is returned
|
|
108
|
+
// for org-less tenant invites rather than a permission 403.
|
|
109
|
+
if (this.isTenantInvite(routeGroup) && !req.organization) {
|
|
110
|
+
throw new common_1.BadRequestException('Organization context required');
|
|
111
|
+
}
|
|
112
|
+
this.assertCanInvite(req, routeGroup, 'store');
|
|
45
113
|
return this.invitations.create({
|
|
46
114
|
email: body.email,
|
|
47
115
|
roleId: body.roleId,
|
|
48
116
|
organization: req.organization,
|
|
49
117
|
invitedBy: req.user,
|
|
118
|
+
routeGroup,
|
|
50
119
|
});
|
|
51
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Whether this invite requires an organization context. We default to YES to
|
|
123
|
+
* preserve legacy behavior (store always required an org). An invite is only
|
|
124
|
+
* treated as non-tenant — and thus org-optional — when membership enforcement
|
|
125
|
+
* is on AND the resolved group is positively a non-tenant group. This keeps
|
|
126
|
+
* every flag-off code path byte-for-byte unchanged.
|
|
127
|
+
*/
|
|
128
|
+
isTenantInvite(routeGroup) {
|
|
129
|
+
if (!this.config.enforceGroupMembership())
|
|
130
|
+
return true;
|
|
131
|
+
if (routeGroup == null)
|
|
132
|
+
return true;
|
|
133
|
+
return this.config.isTenantGroup(routeGroup);
|
|
134
|
+
}
|
|
52
135
|
async resend(req, id) {
|
|
53
|
-
this.
|
|
136
|
+
this.assertCanInvite(req, req.__routeGroup ?? null, 'resend');
|
|
54
137
|
return this.invitations.resend(parseInt(id, 10));
|
|
55
138
|
}
|
|
56
139
|
async cancel(req, id) {
|
|
57
|
-
this.
|
|
140
|
+
this.assertCanInvite(req, req.__routeGroup ?? null, 'cancel');
|
|
58
141
|
return this.invitations.cancel(parseInt(id, 10));
|
|
59
142
|
}
|
|
60
143
|
async accept(body, req) {
|
|
@@ -106,6 +189,8 @@ __decorate([
|
|
|
106
189
|
], InvitationController.prototype, "accept", null);
|
|
107
190
|
exports.InvitationController = InvitationController = __decorate([
|
|
108
191
|
(0, common_1.Controller)('invitations'),
|
|
109
|
-
__metadata("design:paramtypes", [invitation_service_1.InvitationService
|
|
192
|
+
__metadata("design:paramtypes", [invitation_service_1.InvitationService,
|
|
193
|
+
rhino_config_1.RhinoConfigService,
|
|
194
|
+
membership_service_1.MembershipService])
|
|
110
195
|
], InvitationController);
|
|
111
196
|
//# sourceMappingURL=invitation.controller.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invitation.controller.js","sourceRoot":"","sources":["../../src/controllers/invitation.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAUwB;AACxB,uEAAmE;AACnE,oEAAgE;
|
|
1
|
+
{"version":3,"file":"invitation.controller.js","sourceRoot":"","sources":["../../src/controllers/invitation.controller.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HttpException } from '@nestjs/common';
|
|
2
|
-
export type RhinoErrorCode = 'VALIDATION_FAILED' | 'FORBIDDEN_FIELDS' | 'CROSS_TENANT' | 'UNAUTHORIZED' | 'FORBIDDEN' | 'NOT_FOUND' | 'ACTION_DISABLED' | 'UNKNOWN_RESOURCE' | 'INVITATION_EXPIRED' | 'INVITATION_USED' | 'INVITATION_INVALID_ROLE' | 'NESTED_OPERATION_FAILED' | 'INCLUDE_NOT_AUTHORIZED';
|
|
2
|
+
export type RhinoErrorCode = 'VALIDATION_FAILED' | 'FORBIDDEN_FIELDS' | 'CROSS_TENANT' | 'UNAUTHORIZED' | 'FORBIDDEN' | 'NOT_FOUND' | 'ACTION_DISABLED' | 'UNKNOWN_RESOURCE' | 'INVITATION_EXPIRED' | 'INVITATION_USED' | 'INVITATION_INVALID_ROLE' | 'NESTED_OPERATION_FAILED' | 'INCLUDE_NOT_AUTHORIZED' | 'AUTH_REJECTED' | 'MEMBERSHIP_DENIED';
|
|
3
3
|
export interface RhinoErrorBody {
|
|
4
4
|
code: RhinoErrorCode;
|
|
5
5
|
message: string;
|
|
@@ -21,4 +21,15 @@ export declare class RhinoException extends HttpException {
|
|
|
21
21
|
static actionDisabled(action: string): RhinoException;
|
|
22
22
|
static unknownResource(slug: string): RhinoException;
|
|
23
23
|
static includeNotAuthorized(slug: string): RhinoException;
|
|
24
|
+
static authRejected(message?: string, status?: number): RhinoException;
|
|
25
|
+
static membershipDenied(message?: string): RhinoException;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Thrown by a lifecycle hook to reject an otherwise-successful auth action.
|
|
29
|
+
* The controller catches it: for token-issuing actions (login/register) the
|
|
30
|
+
* just-issued token is revoked; the configured `status` (default 403) is then
|
|
31
|
+
* returned to the client. A hook may also throw any other error.
|
|
32
|
+
*/
|
|
33
|
+
export declare class RhinoAuthRejected extends RhinoException {
|
|
34
|
+
constructor(message?: string, status?: number);
|
|
24
35
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RhinoException = void 0;
|
|
3
|
+
exports.RhinoAuthRejected = exports.RhinoException = void 0;
|
|
4
4
|
const common_1 = require("@nestjs/common");
|
|
5
5
|
/**
|
|
6
6
|
* Single exception shape surfaced by the library. Frontends can rely on the
|
|
@@ -42,6 +42,24 @@ class RhinoException extends common_1.HttpException {
|
|
|
42
42
|
static includeNotAuthorized(slug) {
|
|
43
43
|
return new RhinoException('INCLUDE_NOT_AUTHORIZED', `Include not authorized: ${slug}`, 403, { slug });
|
|
44
44
|
}
|
|
45
|
+
static authRejected(message = 'Authentication rejected', status = 403) {
|
|
46
|
+
return new RhinoException('AUTH_REJECTED', message, status);
|
|
47
|
+
}
|
|
48
|
+
static membershipDenied(message = 'You are not a member of this group.') {
|
|
49
|
+
return new RhinoException('MEMBERSHIP_DENIED', message, 403);
|
|
50
|
+
}
|
|
45
51
|
}
|
|
46
52
|
exports.RhinoException = RhinoException;
|
|
53
|
+
/**
|
|
54
|
+
* Thrown by a lifecycle hook to reject an otherwise-successful auth action.
|
|
55
|
+
* The controller catches it: for token-issuing actions (login/register) the
|
|
56
|
+
* just-issued token is revoked; the configured `status` (default 403) is then
|
|
57
|
+
* returned to the client. A hook may also throw any other error.
|
|
58
|
+
*/
|
|
59
|
+
class RhinoAuthRejected extends RhinoException {
|
|
60
|
+
constructor(message = 'Authentication rejected', status = 403) {
|
|
61
|
+
super('AUTH_REJECTED', message, status);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.RhinoAuthRejected = RhinoAuthRejected;
|
|
47
65
|
//# sourceMappingURL=rhino-exception.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rhino-exception.js","sourceRoot":"","sources":["../../src/errors/rhino-exception.ts"],"names":[],"mappings":";;;AAAA,2CAA+C;
|
|
1
|
+
{"version":3,"file":"rhino-exception.js","sourceRoot":"","sources":["../../src/errors/rhino-exception.ts"],"names":[],"mappings":";;;AAAA,2CAA+C;AAyB/C;;;GAGG;AACH,MAAa,cAAe,SAAQ,sBAAa;IACtC,IAAI,CAAiB;IAE9B,YAAY,IAAoB,EAAE,OAAe,EAAE,MAAc,EAAE,OAA6B;QAC9F,MAAM,IAAI,GAAmB,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAChF,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,MAAM,CAAC,gBAAgB,CAAC,MAAgC;QACtD,OAAO,IAAI,cAAc,CAAC,mBAAmB,EAAE,mBAAmB,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACvF,CAAC;IAED,MAAM,CAAC,eAAe,CAAC,MAAgB;QACrC,OAAO,IAAI,cAAc,CAAC,kBAAkB,EAAE,mDAAmD,EAAE,GAAG,EAAE;YACtG,MAAM;SACP,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,MAAgC;QACjD,OAAO,IAAI,cAAc,CAAC,cAAc,EAAE,+CAA+C,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9G,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,OAAO,GAAG,cAAc;QAC1C,OAAO,IAAI,cAAc,CAAC,cAAc,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,CAAC,SAAS,CAAC,OAAO,GAAG,8BAA8B;QACvD,OAAO,IAAI,cAAc,CAAC,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,CAAC,QAAQ,CAAC,OAAO,GAAG,WAAW;QACnC,OAAO,IAAI,cAAc,CAAC,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,MAAc;QAClC,OAAO,IAAI,cAAc,CAAC,iBAAiB,EAAE,sBAAsB,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACxF,CAAC;IAED,MAAM,CAAC,eAAe,CAAC,IAAY;QACjC,OAAO,IAAI,cAAc,CAAC,kBAAkB,EAAE,qBAAqB,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5F,CAAC;IAED,MAAM,CAAC,oBAAoB,CAAC,IAAY;QACtC,OAAO,IAAI,cAAc,CAAC,wBAAwB,EAAE,2BAA2B,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACxG,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,OAAO,GAAG,yBAAyB,EAAE,MAAM,GAAG,GAAG;QACnE,OAAO,IAAI,cAAc,CAAC,eAAe,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9D,CAAC;IAED,MAAM,CAAC,gBAAgB,CAAC,OAAO,GAAG,qCAAqC;QACrE,OAAO,IAAI,cAAc,CAAC,mBAAmB,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IAC/D,CAAC;CACF;AAtDD,wCAsDC;AAED;;;;;GAKG;AACH,MAAa,iBAAkB,SAAQ,cAAc;IACnD,YAAY,OAAO,GAAG,yBAAyB,EAAE,MAAM,GAAG,GAAG;QAC3D,KAAK,CAAC,eAAe,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1C,CAAC;CACF;AAJD,8CAIC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CanActivate, ExecutionContext } from '@nestjs/common';
|
|
2
|
+
import { RhinoConfigService } from '../rhino.config';
|
|
3
|
+
import { MembershipService } from '../services/membership.service';
|
|
4
|
+
/**
|
|
5
|
+
* Coarse group-access gate (design §6), gated entirely by
|
|
6
|
+
* `auth.enforceGroupMembership`.
|
|
7
|
+
*
|
|
8
|
+
* When the flag is OFF this guard is a pure no-op: it returns `true`
|
|
9
|
+
* immediately and never inspects the request, so behavior is byte-for-byte
|
|
10
|
+
* unchanged. Install it AFTER `JwtAuthGuard` (needs `req.user`) and after
|
|
11
|
+
* `RouteGroupMiddleware` (needs `req.__routeGroup`).
|
|
12
|
+
*
|
|
13
|
+
* When ON:
|
|
14
|
+
* - `public`/skip-auth requests bypass the check (no auth).
|
|
15
|
+
* - the user must hold a membership row matching the resolved group (NULL row
|
|
16
|
+
* = wildcard) and, for tenant groups, the resolved org. No match → 403.
|
|
17
|
+
* - on success, the matched rows' permissions become the permission source
|
|
18
|
+
* for the downstream ResourcePolicy (attached as `__membershipPermissions`).
|
|
19
|
+
*/
|
|
20
|
+
export declare class GroupMembershipGuard implements CanActivate {
|
|
21
|
+
private readonly config;
|
|
22
|
+
private readonly membership;
|
|
23
|
+
constructor(config: RhinoConfigService, membership: MembershipService);
|
|
24
|
+
canActivate(context: ExecutionContext): boolean;
|
|
25
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.GroupMembershipGuard = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const rhino_config_1 = require("../rhino.config");
|
|
15
|
+
const membership_service_1 = require("../services/membership.service");
|
|
16
|
+
const rhino_exception_1 = require("../errors/rhino-exception");
|
|
17
|
+
/**
|
|
18
|
+
* Coarse group-access gate (design §6), gated entirely by
|
|
19
|
+
* `auth.enforceGroupMembership`.
|
|
20
|
+
*
|
|
21
|
+
* When the flag is OFF this guard is a pure no-op: it returns `true`
|
|
22
|
+
* immediately and never inspects the request, so behavior is byte-for-byte
|
|
23
|
+
* unchanged. Install it AFTER `JwtAuthGuard` (needs `req.user`) and after
|
|
24
|
+
* `RouteGroupMiddleware` (needs `req.__routeGroup`).
|
|
25
|
+
*
|
|
26
|
+
* When ON:
|
|
27
|
+
* - `public`/skip-auth requests bypass the check (no auth).
|
|
28
|
+
* - the user must hold a membership row matching the resolved group (NULL row
|
|
29
|
+
* = wildcard) and, for tenant groups, the resolved org. No match → 403.
|
|
30
|
+
* - on success, the matched rows' permissions become the permission source
|
|
31
|
+
* for the downstream ResourcePolicy (attached as `__membershipPermissions`).
|
|
32
|
+
*/
|
|
33
|
+
let GroupMembershipGuard = class GroupMembershipGuard {
|
|
34
|
+
config;
|
|
35
|
+
membership;
|
|
36
|
+
constructor(config, membership) {
|
|
37
|
+
this.config = config;
|
|
38
|
+
this.membership = membership;
|
|
39
|
+
}
|
|
40
|
+
canActivate(context) {
|
|
41
|
+
if (!this.membership.enabled())
|
|
42
|
+
return true;
|
|
43
|
+
const req = context.switchToHttp().getRequest();
|
|
44
|
+
if (req.__skipAuth)
|
|
45
|
+
return true;
|
|
46
|
+
const user = req.user;
|
|
47
|
+
if (!user)
|
|
48
|
+
return true; // unauthenticated handling is JwtAuthGuard's job
|
|
49
|
+
const routeGroup = req.__routeGroup ?? null;
|
|
50
|
+
// The `public` group never enforces membership (no auth).
|
|
51
|
+
if (routeGroup === 'public')
|
|
52
|
+
return true;
|
|
53
|
+
const org = req.organization ?? null;
|
|
54
|
+
const isTenant = this.config.isTenantGroup(routeGroup);
|
|
55
|
+
const rows = this.membership.matchingRows(user, routeGroup, org, isTenant);
|
|
56
|
+
if (rows.length === 0) {
|
|
57
|
+
throw rhino_exception_1.RhinoException.membershipDenied();
|
|
58
|
+
}
|
|
59
|
+
// Switch the permission source to the matched membership row(s).
|
|
60
|
+
user.__membershipPermissions = this.membership.permissionsFromRows(rows);
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
exports.GroupMembershipGuard = GroupMembershipGuard;
|
|
65
|
+
exports.GroupMembershipGuard = GroupMembershipGuard = __decorate([
|
|
66
|
+
(0, common_1.Injectable)(),
|
|
67
|
+
__metadata("design:paramtypes", [rhino_config_1.RhinoConfigService,
|
|
68
|
+
membership_service_1.MembershipService])
|
|
69
|
+
], GroupMembershipGuard);
|
|
70
|
+
//# sourceMappingURL=group-membership.guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"group-membership.guard.js","sourceRoot":"","sources":["../../src/guards/group-membership.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAIwB;AACxB,kDAAqD;AACrD,uEAAmE;AACnE,+DAA2D;AAG3D;;;;;;;;;;;;;;;GAeG;AAEI,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAEZ;IACA;IAFnB,YACmB,MAA0B,EAC1B,UAA6B;QAD7B,WAAM,GAAN,MAAM,CAAoB;QAC1B,eAAU,GAAV,UAAU,CAAmB;IAC7C,CAAC;IAEJ,WAAW,CAAC,OAAyB;QACnC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE;YAAE,OAAO,IAAI,CAAC;QAE5C,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAgB,CAAC;QAC9D,IAAI,GAAG,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QAChC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC,CAAC,iDAAiD;QAEzE,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC;QAC5C,0DAA0D;QAC1D,IAAI,UAAU,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAEzC,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QAEvD,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC3E,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,MAAM,gCAAc,CAAC,gBAAgB,EAAE,CAAC;QAC1C,CAAC;QAED,iEAAiE;QAChE,IAAY,CAAC,uBAAuB,GAAG,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAClF,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AA9BY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;qCAGgB,iCAAkB;QACd,sCAAiB;GAHrC,oBAAoB,CA8BhC"}
|
|
@@ -39,12 +39,31 @@ let JwtAuthGuard = class JwtAuthGuard {
|
|
|
39
39
|
throw rhino_exception_1.RhinoException.unauthorized('Missing bearer token');
|
|
40
40
|
}
|
|
41
41
|
const payload = this.auth.verifyToken(token);
|
|
42
|
+
if (await this.auth.isTokenRevoked(token)) {
|
|
43
|
+
throw rhino_exception_1.RhinoException.unauthorized('Token has been revoked');
|
|
44
|
+
}
|
|
42
45
|
const userModel = this.config.authConfig().userModel;
|
|
43
46
|
const delegate = this.prisma.model(userModel);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
// Only eager-load userRoles when multi-tenancy is configured. A
|
|
48
|
+
// single-tenant / org-less app's User model has no `userRoles` relation, so
|
|
49
|
+
// an unconditional include throws inside Prisma — which would make every
|
|
50
|
+
// authenticated request fail with a 401 even though the token is valid.
|
|
51
|
+
// Gate the include and fall back to a plain lookup if it fails. Multi-tenant
|
|
52
|
+
// behavior is byte-for-byte unchanged.
|
|
53
|
+
let user = null;
|
|
54
|
+
if (this.config.multiTenantEnabled()) {
|
|
55
|
+
user = await delegate
|
|
56
|
+
.findUnique({
|
|
57
|
+
where: { id: payload.sub },
|
|
58
|
+
include: { userRoles: { include: { role: true } } },
|
|
59
|
+
})
|
|
60
|
+
.catch(() => null);
|
|
61
|
+
}
|
|
62
|
+
if (!user) {
|
|
63
|
+
user = await delegate
|
|
64
|
+
.findUnique({ where: { id: payload.sub } })
|
|
65
|
+
.catch(() => null);
|
|
66
|
+
}
|
|
48
67
|
if (!user)
|
|
49
68
|
throw rhino_exception_1.RhinoException.unauthorized('User not found');
|
|
50
69
|
req.user = user;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt-auth.guard.js","sourceRoot":"","sources":["../../src/guards/jwt-auth.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAIwB;AACxB,2DAAuD;AACvD,6DAAyD;AACzD,kDAAqD;AACrD,+DAA2D;AAG3D;;;;GAIG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAY;IAEJ;IACA;IACA;IAHnB,YACmB,IAAiB,EACjB,MAAqB,EACrB,MAA0B;QAF1B,SAAI,GAAJ,IAAI,CAAa;QACjB,WAAM,GAAN,MAAM,CAAe;QACrB,WAAM,GAAN,MAAM,CAAoB;IAC1C,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAgB,CAAC;QAC9D,IAAI,GAAG,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,aAAa,IAAI,EAAE,CAAC,CAAC;QACxD,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;YAClC,MAAM,gCAAc,CAAC,YAAY,CAAC,sBAAsB,CAAC,CAAC;QAC5D,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,SAAS,CAAC;QACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,MAAM,QAAQ,
|
|
1
|
+
{"version":3,"file":"jwt-auth.guard.js","sourceRoot":"","sources":["../../src/guards/jwt-auth.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAIwB;AACxB,2DAAuD;AACvD,6DAAyD;AACzD,kDAAqD;AACrD,+DAA2D;AAG3D;;;;GAIG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAY;IAEJ;IACA;IACA;IAHnB,YACmB,IAAiB,EACjB,MAAqB,EACrB,MAA0B;QAF1B,SAAI,GAAJ,IAAI,CAAa;QACjB,WAAM,GAAN,MAAM,CAAe;QACrB,WAAM,GAAN,MAAM,CAAoB;IAC1C,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAgB,CAAC;QAC9D,IAAI,GAAG,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,aAAa,IAAI,EAAE,CAAC,CAAC;QACxD,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;YAClC,MAAM,gCAAc,CAAC,YAAY,CAAC,sBAAsB,CAAC,CAAC;QAC5D,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1C,MAAM,gCAAc,CAAC,YAAY,CAAC,wBAAwB,CAAC,CAAC;QAC9D,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,SAAS,CAAC;QACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC9C,gEAAgE;QAChE,4EAA4E;QAC5E,yEAAyE;QACzE,wEAAwE;QACxE,6EAA6E;QAC7E,uCAAuC;QACvC,IAAI,IAAI,GAAQ,IAAI,CAAC;QACrB,IAAI,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,EAAE,CAAC;YACrC,IAAI,GAAG,MAAM,QAAQ;iBAClB,UAAU,CAAC;gBACV,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,GAAG,EAAE;gBAC1B,OAAO,EAAE,EAAE,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;aACpD,CAAC;iBACD,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,GAAG,MAAM,QAAQ;iBAClB,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;iBAC1C,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QACD,IAAI,CAAC,IAAI;YAAE,MAAM,gCAAc,CAAC,YAAY,CAAC,gBAAgB,CAAC,CAAC;QAC/D,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AA7CY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;qCAGc,0BAAW;QACT,8BAAa;QACb,iCAAkB;GAJlC,YAAY,CA6CxB"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { RhinoModule, type RhinoModuleOptions } from './rhino.module';
|
|
2
|
-
export { RhinoException, type RhinoErrorCode, type RhinoErrorBody, } from './errors/rhino-exception';
|
|
2
|
+
export { RhinoException, RhinoAuthRejected, type RhinoErrorCode, type RhinoErrorBody, } from './errors/rhino-exception';
|
|
3
3
|
export type { RhinoRequest } from './interfaces/rhino-request.interface';
|
|
4
4
|
export { RhinoConfigService, normalizeConfig, } from './rhino.config';
|
|
5
5
|
export { RHINO_CONFIG, RHINO_PRISMA_CLIENT, RHINO_MODULE_OPTIONS, } from './constants/tokens';
|
|
@@ -7,11 +7,13 @@ export * from './interfaces/rhino-config.interface';
|
|
|
7
7
|
export { ResourcePolicy } from './policies/resource-policy';
|
|
8
8
|
export { JwtAuthGuard } from './guards/jwt-auth.guard';
|
|
9
9
|
export { ResourcePolicyGuard } from './guards/resource-policy.guard';
|
|
10
|
+
export { GroupMembershipGuard } from './guards/group-membership.guard';
|
|
10
11
|
export { ResponseInterceptor, paginated } from './interceptors/response.interceptor';
|
|
11
12
|
export { HiddenColumnsInterceptor } from './interceptors/hidden-columns.interceptor';
|
|
12
13
|
export { ResolveOrganizationMiddleware } from './middleware/resolve-organization.middleware';
|
|
13
14
|
export { RouteGroupMiddleware } from './middleware/route-group.middleware';
|
|
14
15
|
export { createTenantRouteRewrite, type TenantRouteRewriteOptions, } from './middleware/tenant-route-rewrite';
|
|
16
|
+
export { createDomainRouteResolver, type DomainRouteResolverOptions, } from './middleware/domain-route-resolver';
|
|
15
17
|
export { GlobalController } from './controllers/global.controller';
|
|
16
18
|
export { AuthController } from './controllers/auth.controller';
|
|
17
19
|
export { InvitationController } from './controllers/invitation.controller';
|
|
@@ -24,6 +26,8 @@ export { ValidationService } from './services/validation.service';
|
|
|
24
26
|
export { AuditService } from './services/audit.service';
|
|
25
27
|
export { AuthService } from './services/auth.service';
|
|
26
28
|
export { InvitationService } from './services/invitation.service';
|
|
29
|
+
export { AuthHooksService, type AuthHookEvent } from './services/auth-hooks.service';
|
|
30
|
+
export { MembershipService, type MembershipRow, } from './services/membership.service';
|
|
27
31
|
export { NestedService } from './services/nested.service';
|
|
28
32
|
export { OrganizationService } from './services/organization.service';
|
|
29
33
|
export { ScopeService, type RhinoScope } from './services/scope.service';
|
|
@@ -33,6 +37,8 @@ export { withUuid } from './prisma/prisma-uuid.extension';
|
|
|
33
37
|
export { RhinoModel, getRhinoModelMetadata, BelongsToOrganization, HasAuditTrail, HasUuid, HasSoftDeletes, ExceptActions, HidableColumns, PermittedAttrs, } from './decorators';
|
|
34
38
|
export { coercePermissions, matchesPermission, resolveUserRoleSlug, resolveUserPermissions, userHasPermission, } from './utils/permission-matcher';
|
|
35
39
|
export { formatPrice, type CurrencyCode } from './utils/format';
|
|
40
|
+
export { compileDomain, matchDomain, type CompiledDomain, type DomainMatch, } from './utils/domain-pattern';
|
|
41
|
+
export { validateRouteGroups, RouteGroupConflictError, } from './utils/route-group-validator';
|
|
36
42
|
export { autoDiscoverScopes, type ScopeDiscoveryOptions } from './utils/scope-discovery';
|
|
37
43
|
export { defineModel, type ModelDefinition } from './utils/model-builder';
|
|
38
44
|
export { findOrganizationFkChain, type FkRelation, type FkChainStep, type WalkOptions, } from './utils/fk-chain-walker';
|
package/dist/index.js
CHANGED
|
@@ -14,14 +14,15 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.
|
|
18
|
-
exports.SeederGenerator = exports.TestGenerator = exports.PolicyGenerator = exports.ResourceDefinitionGenerator = exports.PrismaSchemaGenerator = exports.BlueprintRunner = exports.ManifestManager = exports.BlueprintValidator = exports.BlueprintParser = exports.appendIfMissing = exports.writeFileSafely = exports.fileExists = exports.createReadlineInterface = exports.selectFromList = exports.confirm = exports.ask = exports.runExportTypes = exports.runExportPostman = exports.runBlueprint = exports.runGenerate = exports.runInstall = exports.printHelp = exports.extractCommand = exports.parseFlags = exports.generateTypeScriptTypes = exports.generatePostmanCollection = exports.findOrganizationFkChain = exports.defineModel = exports.autoDiscoverScopes = exports.formatPrice = void 0;
|
|
17
|
+
exports.PermittedAttrs = exports.HidableColumns = exports.ExceptActions = exports.HasSoftDeletes = exports.HasUuid = exports.HasAuditTrail = exports.BelongsToOrganization = exports.getRhinoModelMetadata = exports.RhinoModel = exports.withUuid = exports.withSoftDelete = exports.describeRoutes = exports.applyRhinoRouting = exports.ScopeService = exports.OrganizationService = exports.NestedService = exports.MembershipService = exports.AuthHooksService = exports.InvitationService = exports.AuthService = exports.AuditService = exports.ValidationService = exports.BASE_HIDDEN_COLUMNS = exports.SerializerService = exports.QueryBuilderService = exports.ResourceService = exports.PrismaService = exports.NestedController = exports.InvitationController = exports.AuthController = exports.GlobalController = exports.createDomainRouteResolver = exports.createTenantRouteRewrite = exports.RouteGroupMiddleware = exports.ResolveOrganizationMiddleware = exports.HiddenColumnsInterceptor = exports.paginated = exports.ResponseInterceptor = exports.GroupMembershipGuard = exports.ResourcePolicyGuard = exports.JwtAuthGuard = exports.ResourcePolicy = exports.RHINO_MODULE_OPTIONS = exports.RHINO_PRISMA_CLIENT = exports.RHINO_CONFIG = exports.normalizeConfig = exports.RhinoConfigService = exports.RhinoAuthRejected = exports.RhinoException = exports.RhinoModule = void 0;
|
|
18
|
+
exports.SeederGenerator = exports.TestGenerator = exports.PolicyGenerator = exports.ResourceDefinitionGenerator = exports.PrismaSchemaGenerator = exports.BlueprintRunner = exports.ManifestManager = exports.BlueprintValidator = exports.BlueprintParser = exports.appendIfMissing = exports.writeFileSafely = exports.fileExists = exports.createReadlineInterface = exports.selectFromList = exports.confirm = exports.ask = exports.runExportTypes = exports.runExportPostman = exports.runBlueprint = exports.runGenerate = exports.runInstall = exports.printHelp = exports.extractCommand = exports.parseFlags = exports.generateTypeScriptTypes = exports.generatePostmanCollection = exports.findOrganizationFkChain = exports.defineModel = exports.autoDiscoverScopes = exports.RouteGroupConflictError = exports.validateRouteGroups = exports.matchDomain = exports.compileDomain = exports.formatPrice = exports.userHasPermission = exports.resolveUserPermissions = exports.resolveUserRoleSlug = exports.matchesPermission = exports.coercePermissions = void 0;
|
|
19
19
|
// Module
|
|
20
20
|
var rhino_module_1 = require("./rhino.module");
|
|
21
21
|
Object.defineProperty(exports, "RhinoModule", { enumerable: true, get: function () { return rhino_module_1.RhinoModule; } });
|
|
22
22
|
// Errors
|
|
23
23
|
var rhino_exception_1 = require("./errors/rhino-exception");
|
|
24
24
|
Object.defineProperty(exports, "RhinoException", { enumerable: true, get: function () { return rhino_exception_1.RhinoException; } });
|
|
25
|
+
Object.defineProperty(exports, "RhinoAuthRejected", { enumerable: true, get: function () { return rhino_exception_1.RhinoAuthRejected; } });
|
|
25
26
|
var rhino_config_1 = require("./rhino.config");
|
|
26
27
|
Object.defineProperty(exports, "RhinoConfigService", { enumerable: true, get: function () { return rhino_config_1.RhinoConfigService; } });
|
|
27
28
|
Object.defineProperty(exports, "normalizeConfig", { enumerable: true, get: function () { return rhino_config_1.normalizeConfig; } });
|
|
@@ -40,6 +41,8 @@ var jwt_auth_guard_1 = require("./guards/jwt-auth.guard");
|
|
|
40
41
|
Object.defineProperty(exports, "JwtAuthGuard", { enumerable: true, get: function () { return jwt_auth_guard_1.JwtAuthGuard; } });
|
|
41
42
|
var resource_policy_guard_1 = require("./guards/resource-policy.guard");
|
|
42
43
|
Object.defineProperty(exports, "ResourcePolicyGuard", { enumerable: true, get: function () { return resource_policy_guard_1.ResourcePolicyGuard; } });
|
|
44
|
+
var group_membership_guard_1 = require("./guards/group-membership.guard");
|
|
45
|
+
Object.defineProperty(exports, "GroupMembershipGuard", { enumerable: true, get: function () { return group_membership_guard_1.GroupMembershipGuard; } });
|
|
43
46
|
// Interceptors
|
|
44
47
|
var response_interceptor_1 = require("./interceptors/response.interceptor");
|
|
45
48
|
Object.defineProperty(exports, "ResponseInterceptor", { enumerable: true, get: function () { return response_interceptor_1.ResponseInterceptor; } });
|
|
@@ -53,6 +56,8 @@ var route_group_middleware_1 = require("./middleware/route-group.middleware");
|
|
|
53
56
|
Object.defineProperty(exports, "RouteGroupMiddleware", { enumerable: true, get: function () { return route_group_middleware_1.RouteGroupMiddleware; } });
|
|
54
57
|
var tenant_route_rewrite_1 = require("./middleware/tenant-route-rewrite");
|
|
55
58
|
Object.defineProperty(exports, "createTenantRouteRewrite", { enumerable: true, get: function () { return tenant_route_rewrite_1.createTenantRouteRewrite; } });
|
|
59
|
+
var domain_route_resolver_1 = require("./middleware/domain-route-resolver");
|
|
60
|
+
Object.defineProperty(exports, "createDomainRouteResolver", { enumerable: true, get: function () { return domain_route_resolver_1.createDomainRouteResolver; } });
|
|
56
61
|
// Controllers (consuming apps can re-export under their own routes)
|
|
57
62
|
var global_controller_1 = require("./controllers/global.controller");
|
|
58
63
|
Object.defineProperty(exports, "GlobalController", { enumerable: true, get: function () { return global_controller_1.GlobalController; } });
|
|
@@ -80,6 +85,10 @@ var auth_service_1 = require("./services/auth.service");
|
|
|
80
85
|
Object.defineProperty(exports, "AuthService", { enumerable: true, get: function () { return auth_service_1.AuthService; } });
|
|
81
86
|
var invitation_service_1 = require("./services/invitation.service");
|
|
82
87
|
Object.defineProperty(exports, "InvitationService", { enumerable: true, get: function () { return invitation_service_1.InvitationService; } });
|
|
88
|
+
var auth_hooks_service_1 = require("./services/auth-hooks.service");
|
|
89
|
+
Object.defineProperty(exports, "AuthHooksService", { enumerable: true, get: function () { return auth_hooks_service_1.AuthHooksService; } });
|
|
90
|
+
var membership_service_1 = require("./services/membership.service");
|
|
91
|
+
Object.defineProperty(exports, "MembershipService", { enumerable: true, get: function () { return membership_service_1.MembershipService; } });
|
|
83
92
|
var nested_service_1 = require("./services/nested.service");
|
|
84
93
|
Object.defineProperty(exports, "NestedService", { enumerable: true, get: function () { return nested_service_1.NestedService; } });
|
|
85
94
|
var organization_service_1 = require("./services/organization.service");
|
|
@@ -114,6 +123,12 @@ Object.defineProperty(exports, "resolveUserPermissions", { enumerable: true, get
|
|
|
114
123
|
Object.defineProperty(exports, "userHasPermission", { enumerable: true, get: function () { return permission_matcher_1.userHasPermission; } });
|
|
115
124
|
var format_1 = require("./utils/format");
|
|
116
125
|
Object.defineProperty(exports, "formatPrice", { enumerable: true, get: function () { return format_1.formatPrice; } });
|
|
126
|
+
var domain_pattern_1 = require("./utils/domain-pattern");
|
|
127
|
+
Object.defineProperty(exports, "compileDomain", { enumerable: true, get: function () { return domain_pattern_1.compileDomain; } });
|
|
128
|
+
Object.defineProperty(exports, "matchDomain", { enumerable: true, get: function () { return domain_pattern_1.matchDomain; } });
|
|
129
|
+
var route_group_validator_1 = require("./utils/route-group-validator");
|
|
130
|
+
Object.defineProperty(exports, "validateRouteGroups", { enumerable: true, get: function () { return route_group_validator_1.validateRouteGroups; } });
|
|
131
|
+
Object.defineProperty(exports, "RouteGroupConflictError", { enumerable: true, get: function () { return route_group_validator_1.RouteGroupConflictError; } });
|
|
117
132
|
var scope_discovery_1 = require("./utils/scope-discovery");
|
|
118
133
|
Object.defineProperty(exports, "autoDiscoverScopes", { enumerable: true, get: function () { return scope_discovery_1.autoDiscoverScopes; } });
|
|
119
134
|
var model_builder_1 = require("./utils/model-builder");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,SAAS;AACT,+CAAsE;AAA7D,2GAAA,WAAW,OAAA;AAEpB,SAAS;AACT,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|