@rhino-dev/rhino-nestjs 4.0.0 → 4.2.0
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/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,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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"}
|
|
@@ -36,13 +36,74 @@ export interface ModelRegistration {
|
|
|
36
36
|
model: string;
|
|
37
37
|
}>;
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Context handed to every lifecycle hook. `routeGroup` is the resolved group
|
|
41
|
+
* name (or `null`/`undefined` for the legacy/global auth path), `organization`
|
|
42
|
+
* is present only for tenant groups, `token` is the just-issued JWT for
|
|
43
|
+
* token-issuing actions (login/register), and `request` is the raw request.
|
|
44
|
+
*/
|
|
45
|
+
export interface AuthHookContext {
|
|
46
|
+
user: any;
|
|
47
|
+
routeGroup?: string | null;
|
|
48
|
+
organization?: any;
|
|
49
|
+
token?: string;
|
|
50
|
+
request?: any;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Per-group lifecycle hooks. Each method runs AFTER the corresponding auth
|
|
54
|
+
* action succeeds. A method may reject by throwing `RhinoAuthRejected` (or any
|
|
55
|
+
* error) — for token-issuing actions the controller revokes the issued token
|
|
56
|
+
* and returns the rejection's status (default 403). All methods are optional;
|
|
57
|
+
* an absent method is a no-op. Implementations are registered per group via
|
|
58
|
+
* `RouteGroupConfig.hooks` and resolved from the Nest DI container.
|
|
59
|
+
*/
|
|
60
|
+
export interface AuthLifecycleHooks {
|
|
61
|
+
afterLogin?(ctx: AuthHookContext): void | Promise<void>;
|
|
62
|
+
afterLogout?(ctx: AuthHookContext): void | Promise<void>;
|
|
63
|
+
afterRegister?(ctx: AuthHookContext): void | Promise<void>;
|
|
64
|
+
afterPasswordRecover?(ctx: AuthHookContext): void | Promise<void>;
|
|
65
|
+
afterPasswordReset?(ctx: AuthHookContext): void | Promise<void>;
|
|
66
|
+
}
|
|
39
67
|
export interface RouteGroupConfig {
|
|
40
68
|
prefix?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Constrain this group to a specific host. Two groups can then share the
|
|
71
|
+
* same URL prefix and be selected by host.
|
|
72
|
+
*
|
|
73
|
+
* - Omitted → the group matches any host (default; backward compatible).
|
|
74
|
+
* - Literal host, e.g. `'admin.example.com'` → requests to that host
|
|
75
|
+
* resolve to this group; requests from a non-matching host are rejected.
|
|
76
|
+
* - Parameterized host, e.g. `'{organization}.example.com'` → the captured
|
|
77
|
+
* `{organization}` subdomain feeds organization resolution, exactly like
|
|
78
|
+
* a path-prefix tenant param. Matches Laravel's `Route::domain(...)`.
|
|
79
|
+
*/
|
|
80
|
+
domain?: string;
|
|
41
81
|
middleware?: Type<NestMiddleware>[];
|
|
42
82
|
/** '*' = all registered models, array = subset by slug */
|
|
43
83
|
models: '*' | string[];
|
|
44
84
|
/** Skip the default JWT guard for this group (for public routes) */
|
|
45
85
|
skipAuth?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Register the full auth route set (login/logout/password/register) for this
|
|
88
|
+
* group, tagged with the group's name (Decision 9.A). The legacy unprefixed
|
|
89
|
+
* `/auth/*` set always remains for the default/global path. Opt-in; default
|
|
90
|
+
* `false`. The `public` group is never auth-enabled.
|
|
91
|
+
*/
|
|
92
|
+
auth?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Optional per-group lifecycle hooks. A class (resolved via Nest DI) or a
|
|
95
|
+
* plain object implementing {@link AuthLifecycleHooks}. Runs after each auth
|
|
96
|
+
* action for requests resolved to this group; may reject to revoke the token.
|
|
97
|
+
*/
|
|
98
|
+
hooks?: Type<AuthLifecycleHooks> | AuthLifecycleHooks;
|
|
99
|
+
/**
|
|
100
|
+
* Whether this group is org-scoped (a tenant group). Tenant-group membership
|
|
101
|
+
* rows require an organization; non-tenant groups (e.g. `admin`, `driver`)
|
|
102
|
+
* store a NULL org. When omitted, a group is treated as a tenant group iff
|
|
103
|
+
* multi-tenancy is enabled. Set `tenant: false` for org-less groups even when
|
|
104
|
+
* multi-tenancy is on.
|
|
105
|
+
*/
|
|
106
|
+
tenant?: boolean;
|
|
46
107
|
}
|
|
47
108
|
export interface MultiTenantConfig {
|
|
48
109
|
enabled?: boolean;
|
|
@@ -66,6 +127,16 @@ export interface AuthConfig {
|
|
|
66
127
|
userModel?: string;
|
|
67
128
|
passwordField?: string;
|
|
68
129
|
emailField?: string;
|
|
130
|
+
/**
|
|
131
|
+
* Master flag (default `false`) gating group-membership enforcement. When
|
|
132
|
+
* off, behavior is byte-for-byte unchanged: no membership check, permissions
|
|
133
|
+
* resolve from the org-presence heuristic. When on, an authenticated user
|
|
134
|
+
* must hold a `user_roles` membership row matching the request's
|
|
135
|
+
* `route_group` (a NULL row is a wildcard — Decision 9.B) and, for tenant
|
|
136
|
+
* groups, the resolved org; no match → 403 (Decision 9.C). Permissions then
|
|
137
|
+
* resolve from the matched row.
|
|
138
|
+
*/
|
|
139
|
+
enforceGroupMembership?: boolean;
|
|
69
140
|
}
|
|
70
141
|
export interface PostmanConfig {
|
|
71
142
|
roleModel?: string;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import type { RhinoConfig } from '../interfaces/rhino-config.interface';
|
|
3
|
+
import type { PrismaClientLike } from '../prisma/prisma.service';
|
|
4
|
+
export interface DomainRouteResolverOptions {
|
|
5
|
+
/**
|
|
6
|
+
* When `true` (default), a request whose host matches a parameterized
|
|
7
|
+
* domain group but whose captured subdomain resolves to no known
|
|
8
|
+
* organization → HTTP 404. When `false`, the request passes through
|
|
9
|
+
* unchanged (org simply isn't attached).
|
|
10
|
+
*/
|
|
11
|
+
strict?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* When `true` (default), after resolving the org the resolver verifies the
|
|
14
|
+
* authenticated user (if any) is a member. Non-members → 404 (matches the
|
|
15
|
+
* tenant-route-rewrite contract: cross-tenant = "not found", no
|
|
16
|
+
* enumeration). When `req.user` is absent the check is skipped (auth runs
|
|
17
|
+
* later).
|
|
18
|
+
*/
|
|
19
|
+
enforceMembership?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Factory for an Express-level middleware that implements host-based route
|
|
23
|
+
* group resolution — the subdomain-multitenancy analogue of
|
|
24
|
+
* `createTenantRouteRewrite`.
|
|
25
|
+
*
|
|
26
|
+
* Context: NestJS serves every model through a single GlobalController, and a
|
|
27
|
+
* route group is resolved at request time. For a parameterized domain group
|
|
28
|
+
* (`{organization}.example.com`), the captured subdomain must be turned into a
|
|
29
|
+
* resolved `req.organization` BEFORE the controller runs — exactly like the
|
|
30
|
+
* path-based `/api/<slug>/...` rewrite does for the `:organization` prefix.
|
|
31
|
+
*
|
|
32
|
+
* Behavior:
|
|
33
|
+
*
|
|
34
|
+
* 1. For each route group with a `domain`, compile its pattern.
|
|
35
|
+
* 2. On each request, match `req.hostname` against those patterns.
|
|
36
|
+
* - No domain group matches → next() (plain prefix routing applies).
|
|
37
|
+
* 3. When a parameterized domain group matches, take the captured
|
|
38
|
+
* `{organization}` (or `{org}`) subdomain and:
|
|
39
|
+
* - expose it on `req.params.organization`,
|
|
40
|
+
* - resolve the org by `organizationIdentifierColumn` (cached),
|
|
41
|
+
* strict-404 when unknown,
|
|
42
|
+
* - enforce membership when `req.user` is present,
|
|
43
|
+
* - attach `req.organization`.
|
|
44
|
+
* A literal domain group matches but captures no params, so it only marks
|
|
45
|
+
* the host as recognized (no org resolution).
|
|
46
|
+
*
|
|
47
|
+
* Intended for raw Express `app.use(createDomainRouteResolver(...))` BEFORE
|
|
48
|
+
* `applyRhinoRouting(...)`, alongside / instead of `createTenantRouteRewrite`.
|
|
49
|
+
*/
|
|
50
|
+
export declare function createDomainRouteResolver(args: {
|
|
51
|
+
prisma: PrismaClientLike;
|
|
52
|
+
config: RhinoConfig;
|
|
53
|
+
options?: DomainRouteResolverOptions;
|
|
54
|
+
}): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDomainRouteResolver = createDomainRouteResolver;
|
|
4
|
+
const domain_pattern_1 = require("../utils/domain-pattern");
|
|
5
|
+
/**
|
|
6
|
+
* Factory for an Express-level middleware that implements host-based route
|
|
7
|
+
* group resolution — the subdomain-multitenancy analogue of
|
|
8
|
+
* `createTenantRouteRewrite`.
|
|
9
|
+
*
|
|
10
|
+
* Context: NestJS serves every model through a single GlobalController, and a
|
|
11
|
+
* route group is resolved at request time. For a parameterized domain group
|
|
12
|
+
* (`{organization}.example.com`), the captured subdomain must be turned into a
|
|
13
|
+
* resolved `req.organization` BEFORE the controller runs — exactly like the
|
|
14
|
+
* path-based `/api/<slug>/...` rewrite does for the `:organization` prefix.
|
|
15
|
+
*
|
|
16
|
+
* Behavior:
|
|
17
|
+
*
|
|
18
|
+
* 1. For each route group with a `domain`, compile its pattern.
|
|
19
|
+
* 2. On each request, match `req.hostname` against those patterns.
|
|
20
|
+
* - No domain group matches → next() (plain prefix routing applies).
|
|
21
|
+
* 3. When a parameterized domain group matches, take the captured
|
|
22
|
+
* `{organization}` (or `{org}`) subdomain and:
|
|
23
|
+
* - expose it on `req.params.organization`,
|
|
24
|
+
* - resolve the org by `organizationIdentifierColumn` (cached),
|
|
25
|
+
* strict-404 when unknown,
|
|
26
|
+
* - enforce membership when `req.user` is present,
|
|
27
|
+
* - attach `req.organization`.
|
|
28
|
+
* A literal domain group matches but captures no params, so it only marks
|
|
29
|
+
* the host as recognized (no org resolution).
|
|
30
|
+
*
|
|
31
|
+
* Intended for raw Express `app.use(createDomainRouteResolver(...))` BEFORE
|
|
32
|
+
* `applyRhinoRouting(...)`, alongside / instead of `createTenantRouteRewrite`.
|
|
33
|
+
*/
|
|
34
|
+
function createDomainRouteResolver(args) {
|
|
35
|
+
const { strict = true, enforceMembership = true } = args.options ?? {};
|
|
36
|
+
// FIX 11.2: when group-membership enforcement is ON, an authenticated
|
|
37
|
+
// non-member must get a 403 from GroupMembershipGuard, which takes precedence
|
|
38
|
+
// over this resolver's cross-tenant 404. So we resolve & attach the org but
|
|
39
|
+
// skip the membership-404 here, deferring to the downstream guard. A
|
|
40
|
+
// genuinely unknown subdomain still 404s (strict). When enforcement is OFF
|
|
41
|
+
// (default), behavior is byte-for-byte unchanged.
|
|
42
|
+
const groupMembershipEnforced = args.config.auth?.enforceGroupMembership === true;
|
|
43
|
+
const orgModel = args.config.multiTenant?.organizationModel ?? 'organization';
|
|
44
|
+
const userOrgModel = args.config.multiTenant?.userOrganizationModel ?? 'userRole';
|
|
45
|
+
const idColumn = args.config.multiTenant?.organizationIdentifierColumn ?? 'slug';
|
|
46
|
+
const compiledGroups = [];
|
|
47
|
+
for (const [name, group] of Object.entries(args.config.routeGroups ?? {})) {
|
|
48
|
+
const compiled = (0, domain_pattern_1.compileDomain)(group.domain);
|
|
49
|
+
if (compiled)
|
|
50
|
+
compiledGroups.push({ name, group, compiled });
|
|
51
|
+
}
|
|
52
|
+
// Cache org lookups by captured subdomain value. `null` = probed, not found.
|
|
53
|
+
const cache = new Map();
|
|
54
|
+
const notFound = (res) => {
|
|
55
|
+
res.status(404).json({ code: 'NOT_FOUND', message: 'Organization not found' });
|
|
56
|
+
};
|
|
57
|
+
return async function domainRouteResolver(req, res, next) {
|
|
58
|
+
if (compiledGroups.length === 0)
|
|
59
|
+
return next();
|
|
60
|
+
try {
|
|
61
|
+
const host = resolveHost(req);
|
|
62
|
+
if (!host)
|
|
63
|
+
return next();
|
|
64
|
+
let matched = null;
|
|
65
|
+
for (const cg of compiledGroups) {
|
|
66
|
+
const result = (0, domain_pattern_1.matchDomain)(cg.compiled, host);
|
|
67
|
+
if (result) {
|
|
68
|
+
matched = { group: cg, params: result.params };
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!matched)
|
|
73
|
+
return next();
|
|
74
|
+
// Mark which group the host resolved to (parity with RouteGroupMiddleware).
|
|
75
|
+
req.__routeGroup = matched.group.name;
|
|
76
|
+
if (matched.group.group.skipAuth)
|
|
77
|
+
req.__skipAuth = true;
|
|
78
|
+
const rawSubdomain = matched.params.organization ?? matched.params.org;
|
|
79
|
+
if (rawSubdomain == null) {
|
|
80
|
+
// Literal domain group — no tenant to resolve.
|
|
81
|
+
return next();
|
|
82
|
+
}
|
|
83
|
+
// DNS hostnames are case-insensitive; orgs are slugged lowercase. Normalize
|
|
84
|
+
// the captured subdomain before the lookup, the cache key, and the param so
|
|
85
|
+
// `ACME.example.com` resolves the `acme` org consistently.
|
|
86
|
+
const subdomain = rawSubdomain.toLowerCase();
|
|
87
|
+
// Expose the captured subdomain like a path param.
|
|
88
|
+
req.params = req.params ?? {};
|
|
89
|
+
if (req.params.organization == null) {
|
|
90
|
+
req.params.organization = subdomain;
|
|
91
|
+
}
|
|
92
|
+
// Resolve the org from the subdomain (cached).
|
|
93
|
+
if (!cache.has(subdomain)) {
|
|
94
|
+
const delegate = args.prisma[camel(orgModel)] ?? args.prisma[orgModel];
|
|
95
|
+
if (!delegate?.findFirst) {
|
|
96
|
+
// No org delegate — can't resolve. Pass through.
|
|
97
|
+
return next();
|
|
98
|
+
}
|
|
99
|
+
const org = await delegate
|
|
100
|
+
.findFirst({ where: { [idColumn]: subdomain } })
|
|
101
|
+
.catch(() => null);
|
|
102
|
+
cache.set(subdomain, org ?? null);
|
|
103
|
+
}
|
|
104
|
+
const org = cache.get(subdomain);
|
|
105
|
+
if (!org) {
|
|
106
|
+
if (strict)
|
|
107
|
+
return notFound(res);
|
|
108
|
+
return next();
|
|
109
|
+
}
|
|
110
|
+
// Membership check (cross-tenant → 404, not 403). Skipped when group
|
|
111
|
+
// membership enforcement is ON — the GroupMembershipGuard returns 403 for
|
|
112
|
+
// an authenticated non-member, which takes precedence (FIX 11.2).
|
|
113
|
+
if (enforceMembership && !groupMembershipEnforced && req.user) {
|
|
114
|
+
const userId = req.user.id;
|
|
115
|
+
const urDelegate = args.prisma[camel(userOrgModel)] ?? args.prisma[userOrgModel];
|
|
116
|
+
if (urDelegate?.findFirst) {
|
|
117
|
+
const member = await urDelegate
|
|
118
|
+
.findFirst({ where: { userId, organizationId: org.id } })
|
|
119
|
+
.catch(() => null);
|
|
120
|
+
if (!member)
|
|
121
|
+
return notFound(res);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
req.organization = org;
|
|
125
|
+
req.__orgSubdomain = subdomain;
|
|
126
|
+
return next();
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
return next(err);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function resolveHost(req) {
|
|
134
|
+
const fromExpress = req.hostname;
|
|
135
|
+
if (fromExpress)
|
|
136
|
+
return String(fromExpress).split(':')[0];
|
|
137
|
+
const header = req.headers?.host ?? req.host;
|
|
138
|
+
return header ? String(header).split(':')[0] : undefined;
|
|
139
|
+
}
|
|
140
|
+
function camel(name) {
|
|
141
|
+
if (!name)
|
|
142
|
+
return name;
|
|
143
|
+
return name.charAt(0).toLowerCase() + name.slice(1);
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=domain-route-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-route-resolver.js","sourceRoot":"","sources":["../../src/middleware/domain-route-resolver.ts"],"names":[],"mappings":"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"}
|