@spinajs/rbac-http-user 2.0.473 → 2.0.474

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.
Files changed (75) hide show
  1. package/lib/cjs/controllers/ActiveRoleController.d.ts +41 -0
  2. package/lib/cjs/controllers/ActiveRoleController.d.ts.map +1 -0
  3. package/lib/cjs/controllers/ActiveRoleController.js +135 -0
  4. package/lib/cjs/controllers/ActiveRoleController.js.map +1 -0
  5. package/lib/cjs/controllers/ImpersonationController.d.ts +72 -0
  6. package/lib/cjs/controllers/ImpersonationController.d.ts.map +1 -0
  7. package/lib/cjs/controllers/ImpersonationController.js +277 -0
  8. package/lib/cjs/controllers/ImpersonationController.js.map +1 -0
  9. package/lib/cjs/controllers/LoginController.d.ts +27 -4
  10. package/lib/cjs/controllers/LoginController.d.ts.map +1 -1
  11. package/lib/cjs/controllers/LoginController.js +46 -27
  12. package/lib/cjs/controllers/LoginController.js.map +1 -1
  13. package/lib/cjs/dto/impersonate-dto.d.ts +24 -0
  14. package/lib/cjs/dto/impersonate-dto.d.ts.map +1 -0
  15. package/lib/cjs/dto/impersonate-dto.js +34 -0
  16. package/lib/cjs/dto/impersonate-dto.js.map +1 -0
  17. package/lib/cjs/dto/switchRole-dto.d.ts +24 -0
  18. package/lib/cjs/dto/switchRole-dto.d.ts.map +1 -0
  19. package/lib/cjs/dto/switchRole-dto.js +34 -0
  20. package/lib/cjs/dto/switchRole-dto.js.map +1 -0
  21. package/lib/cjs/handlers/DefaultLogoutHandler.d.ts +14 -0
  22. package/lib/cjs/handlers/DefaultLogoutHandler.d.ts.map +1 -0
  23. package/lib/cjs/handlers/DefaultLogoutHandler.js +61 -0
  24. package/lib/cjs/handlers/DefaultLogoutHandler.js.map +1 -0
  25. package/lib/cjs/handlers/ImpersonationLogoutHandler.d.ts +18 -0
  26. package/lib/cjs/handlers/ImpersonationLogoutHandler.d.ts.map +1 -0
  27. package/lib/cjs/handlers/ImpersonationLogoutHandler.js +66 -0
  28. package/lib/cjs/handlers/ImpersonationLogoutHandler.js.map +1 -0
  29. package/lib/cjs/index.d.ts +5 -0
  30. package/lib/cjs/index.d.ts.map +1 -1
  31. package/lib/cjs/index.js +5 -0
  32. package/lib/cjs/index.js.map +1 -1
  33. package/lib/cjs/logout.d.ts +51 -0
  34. package/lib/cjs/logout.d.ts.map +1 -0
  35. package/lib/cjs/logout.js +29 -0
  36. package/lib/cjs/logout.js.map +1 -0
  37. package/lib/mjs/controllers/ActiveRoleController.d.ts +41 -0
  38. package/lib/mjs/controllers/ActiveRoleController.d.ts.map +1 -0
  39. package/lib/mjs/controllers/ActiveRoleController.js +132 -0
  40. package/lib/mjs/controllers/ActiveRoleController.js.map +1 -0
  41. package/lib/mjs/controllers/ImpersonationController.d.ts +72 -0
  42. package/lib/mjs/controllers/ImpersonationController.d.ts.map +1 -0
  43. package/lib/mjs/controllers/ImpersonationController.js +274 -0
  44. package/lib/mjs/controllers/ImpersonationController.js.map +1 -0
  45. package/lib/mjs/controllers/LoginController.d.ts +27 -4
  46. package/lib/mjs/controllers/LoginController.d.ts.map +1 -1
  47. package/lib/mjs/controllers/LoginController.js +48 -29
  48. package/lib/mjs/controllers/LoginController.js.map +1 -1
  49. package/lib/mjs/dto/impersonate-dto.d.ts +24 -0
  50. package/lib/mjs/dto/impersonate-dto.d.ts.map +1 -0
  51. package/lib/mjs/dto/impersonate-dto.js +31 -0
  52. package/lib/mjs/dto/impersonate-dto.js.map +1 -0
  53. package/lib/mjs/dto/switchRole-dto.d.ts +24 -0
  54. package/lib/mjs/dto/switchRole-dto.d.ts.map +1 -0
  55. package/lib/mjs/dto/switchRole-dto.js +31 -0
  56. package/lib/mjs/dto/switchRole-dto.js.map +1 -0
  57. package/lib/mjs/handlers/DefaultLogoutHandler.d.ts +14 -0
  58. package/lib/mjs/handlers/DefaultLogoutHandler.d.ts.map +1 -0
  59. package/lib/mjs/handlers/DefaultLogoutHandler.js +58 -0
  60. package/lib/mjs/handlers/DefaultLogoutHandler.js.map +1 -0
  61. package/lib/mjs/handlers/ImpersonationLogoutHandler.d.ts +18 -0
  62. package/lib/mjs/handlers/ImpersonationLogoutHandler.d.ts.map +1 -0
  63. package/lib/mjs/handlers/ImpersonationLogoutHandler.js +63 -0
  64. package/lib/mjs/handlers/ImpersonationLogoutHandler.js.map +1 -0
  65. package/lib/mjs/index.d.ts +5 -0
  66. package/lib/mjs/index.d.ts.map +1 -1
  67. package/lib/mjs/index.js +5 -0
  68. package/lib/mjs/index.js.map +1 -1
  69. package/lib/mjs/logout.d.ts +51 -0
  70. package/lib/mjs/logout.d.ts.map +1 -0
  71. package/lib/mjs/logout.js +25 -0
  72. package/lib/mjs/logout.js.map +1 -0
  73. package/lib/tsconfig.cjs.tsbuildinfo +1 -1
  74. package/lib/tsconfig.mjs.tsbuildinfo +1 -1
  75. package/package.json +11 -11
@@ -0,0 +1,41 @@
1
+ import { SwitchRoleDto } from '../dto/switchRole-dto.js';
2
+ import { BaseController, Ok, BadRequestResponse, Unauthorized } from '@spinajs/http';
3
+ import { AccessControl, AuthProvider, PasswordProvider, SessionProvider } from '@spinajs/rbac';
4
+ import type { ISession, User } from '@spinajs/rbac';
5
+ import { IActiveRoleResponse } from '@spinajs/rbac-http';
6
+ /**
7
+ * Active role endpoints.
8
+ * Let users with multiple roles inspect and switch the currently active role.
9
+ * The active role drives all request-bound permission checks; the user's full
10
+ * role list remains available so they can switch back at any time.
11
+ * @tags Authentication
12
+ */
13
+ export declare class ActiveRoleController extends BaseController {
14
+ protected AC: AccessControl;
15
+ protected AuthProvider: AuthProvider;
16
+ protected PasswordProvider: PasswordProvider;
17
+ protected SessionProvider: SessionProvider;
18
+ protected RolesRequiringPassword: string[];
19
+ /**
20
+ * Get active role
21
+ * Returns the currently active role for the session, all roles the user may switch to,
22
+ * and the RBAC grants resolved for the active role.
23
+ * @security cookieAuth
24
+ * @returns {IActiveRoleResponse}
25
+ * @response 401 No active session
26
+ */
27
+ getActiveRole(user: User, ActiveRole: string): Promise<Ok<IActiveRoleResponse>>;
28
+ /**
29
+ * Switch active role
30
+ * Switches the active role for the current session. The requested role must be one of
31
+ * the user's assigned roles. If the role is listed in `rbac.roleSwitch.requirePassword`,
32
+ * the user's password must also be provided and is re-verified.
33
+ * @security cookieAuth
34
+ * @returns {IActiveRoleResponse}
35
+ * @response 400 Requested role is not assigned to the user
36
+ * @response 401 Password verification required or failed
37
+ */
38
+ switchActiveRole(user: User, session: ISession, payload: SwitchRoleDto): Promise<Ok<IActiveRoleResponse> | BadRequestResponse | Unauthorized>;
39
+ protected buildResponse(user: User, activeRole: string): IActiveRoleResponse;
40
+ }
41
+ //# sourceMappingURL=ActiveRoleController.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActiveRoleController.d.ts","sourceRoot":"","sources":["../../../src/controllers/ActiveRoleController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAwB,EAAE,EAAO,kBAAkB,EAAE,YAAY,EAAU,MAAM,eAAe,CAAC;AACxH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAiB,MAAM,eAAe,CAAC;AAC9G,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAGpD,OAAO,EAA+E,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEtI;;;;;;GAMG;AACH,qBACa,oBAAqB,SAAQ,cAAc;IAEtD,SAAS,CAAC,EAAE,EAAE,aAAa,CAAC;IAG5B,SAAS,CAAC,YAAY,EAAE,YAAY,CAAC;IAGrC,SAAS,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAG7C,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC;IAG3C,SAAS,CAAC,sBAAsB,EAAE,MAAM,EAAE,CAAC;IAE3C;;;;;;;OAOG;IAGU,aAAa,CAAiB,IAAI,EAAE,IAAI,EAAiB,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,mBAAmB,CAAC,CAAC;IAI3H;;;;;;;;;OASG;IAGU,gBAAgB,CACX,IAAI,EAAE,IAAI,EACP,OAAO,EAAE,QAAQ,EAC5B,OAAO,EAAE,aAAa,GAC7B,OAAO,CAAC,EAAE,CAAC,mBAAmB,CAAC,GAAG,kBAAkB,GAAG,YAAY,CAAC;IAqCvE,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,mBAAmB;CAQ7E"}
@@ -0,0 +1,132 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { SwitchRoleDto } from '../dto/switchRole-dto.js';
14
+ import { BaseController, BasePath, Post, Body, Ok, Get, BadRequestResponse, Unauthorized, Policy } from '@spinajs/http';
15
+ import { AccessControl, AuthProvider, PasswordProvider, SessionProvider, _unwindGrants } from '@spinajs/rbac';
16
+ import { Autoinject } from '@spinajs/di';
17
+ import { AutoinjectService, Config } from '@spinajs/configuration';
18
+ import { LoggedPolicy, User as UserRouteArg, Session as SessionRouteArg, FromSession } from '@spinajs/rbac-http';
19
+ /**
20
+ * Active role endpoints.
21
+ * Let users with multiple roles inspect and switch the currently active role.
22
+ * The active role drives all request-bound permission checks; the user's full
23
+ * role list remains available so they can switch back at any time.
24
+ * @tags Authentication
25
+ */
26
+ let ActiveRoleController = class ActiveRoleController extends BaseController {
27
+ /**
28
+ * Get active role
29
+ * Returns the currently active role for the session, all roles the user may switch to,
30
+ * and the RBAC grants resolved for the active role.
31
+ * @security cookieAuth
32
+ * @returns {IActiveRoleResponse}
33
+ * @response 401 No active session
34
+ */
35
+ async getActiveRole(user, ActiveRole) {
36
+ return new Ok(this.buildResponse(user, ActiveRole ?? user.Role?.[0]));
37
+ }
38
+ /**
39
+ * Switch active role
40
+ * Switches the active role for the current session. The requested role must be one of
41
+ * the user's assigned roles. If the role is listed in `rbac.roleSwitch.requirePassword`,
42
+ * the user's password must also be provided and is re-verified.
43
+ * @security cookieAuth
44
+ * @returns {IActiveRoleResponse}
45
+ * @response 400 Requested role is not assigned to the user
46
+ * @response 401 Password verification required or failed
47
+ */
48
+ async switchActiveRole(user, session, payload) {
49
+ if (!user.Role?.includes(payload.Role)) {
50
+ return new BadRequestResponse({
51
+ error: {
52
+ code: 'E_ROLE_NOT_ASSIGNED',
53
+ message: `Role '${payload.Role}' is not assigned to user`,
54
+ },
55
+ });
56
+ }
57
+ if (this.RolesRequiringPassword?.includes(payload.Role)) {
58
+ if (!payload.Password) {
59
+ return new Unauthorized({
60
+ error: {
61
+ code: 'E_PASSWORD_REQUIRED',
62
+ message: `Password is required to activate role '${payload.Role}'`,
63
+ },
64
+ });
65
+ }
66
+ const valid = await this.PasswordProvider.verify(user.Password, payload.Password);
67
+ if (!valid) {
68
+ return new Unauthorized({
69
+ error: {
70
+ code: 'E_PASSWORD_INVALID',
71
+ message: 'Invalid password',
72
+ },
73
+ });
74
+ }
75
+ }
76
+ session.Data.set('ActiveRole', payload.Role);
77
+ await this.SessionProvider.save(session);
78
+ return new Ok(this.buildResponse(user, payload.Role));
79
+ }
80
+ buildResponse(user, activeRole) {
81
+ const grants = activeRole ? _unwindGrants(activeRole, this.AC.getGrants()) : {};
82
+ return {
83
+ ActiveRole: activeRole,
84
+ AvailableRoles: user.Role ?? [],
85
+ Grants: grants,
86
+ };
87
+ }
88
+ };
89
+ __decorate([
90
+ Autoinject(AccessControl),
91
+ __metadata("design:type", AccessControl)
92
+ ], ActiveRoleController.prototype, "AC", void 0);
93
+ __decorate([
94
+ AutoinjectService('rbac.auth'),
95
+ __metadata("design:type", AuthProvider)
96
+ ], ActiveRoleController.prototype, "AuthProvider", void 0);
97
+ __decorate([
98
+ AutoinjectService('rbac.password'),
99
+ __metadata("design:type", PasswordProvider)
100
+ ], ActiveRoleController.prototype, "PasswordProvider", void 0);
101
+ __decorate([
102
+ AutoinjectService('rbac.session'),
103
+ __metadata("design:type", SessionProvider)
104
+ ], ActiveRoleController.prototype, "SessionProvider", void 0);
105
+ __decorate([
106
+ Config('rbac.roleSwitch.requirePassword', { defaultValue: [] }),
107
+ __metadata("design:type", Array)
108
+ ], ActiveRoleController.prototype, "RolesRequiringPassword", void 0);
109
+ __decorate([
110
+ Get('active-role'),
111
+ Policy(LoggedPolicy),
112
+ __param(0, UserRouteArg()),
113
+ __param(1, FromSession()),
114
+ __metadata("design:type", Function),
115
+ __metadata("design:paramtypes", [Function, String]),
116
+ __metadata("design:returntype", Promise)
117
+ ], ActiveRoleController.prototype, "getActiveRole", null);
118
+ __decorate([
119
+ Post('active-role'),
120
+ Policy(LoggedPolicy),
121
+ __param(0, UserRouteArg()),
122
+ __param(1, SessionRouteArg()),
123
+ __param(2, Body()),
124
+ __metadata("design:type", Function),
125
+ __metadata("design:paramtypes", [Function, Object, SwitchRoleDto]),
126
+ __metadata("design:returntype", Promise)
127
+ ], ActiveRoleController.prototype, "switchActiveRole", null);
128
+ ActiveRoleController = __decorate([
129
+ BasePath('auth')
130
+ ], ActiveRoleController);
131
+ export { ActiveRoleController };
132
+ //# sourceMappingURL=ActiveRoleController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActiveRoleController.js","sourceRoot":"","sources":["../../../src/controllers/ActiveRoleController.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import { ImpersonateDto } from '../dto/impersonate-dto.js';
2
+ import { BaseController, Ok, BadRequestResponse, Unauthorized, ForbiddenResponse, Conflict, NotFound } from '@spinajs/http';
3
+ import { AccessControl, PasswordProvider, SessionProvider, User, UserImpersonationStarted, UserImpersonationEnded } from '@spinajs/rbac';
4
+ import type { ISession } from '@spinajs/rbac';
5
+ import { IImpersonationResponse, IImpersonationState, IUserWithGrants } from '@spinajs/rbac-http';
6
+ /**
7
+ * Impersonation endpoints.
8
+ *
9
+ * A user holding `createAny` on the virtual resource `user:impersonate` can
10
+ * temporarily act as another user. While impersonation is active, the session
11
+ * carries both identities — `User` is the target, `Impersonator` is the
12
+ * original. Permission checks therefore "see" the target by default; the
13
+ * original is preserved only for audit and for ending the impersonation.
14
+ *
15
+ * @tags Authentication
16
+ */
17
+ export declare class ImpersonationController extends BaseController {
18
+ protected AC: AccessControl;
19
+ protected PasswordProvider: PasswordProvider;
20
+ protected SessionProvider: SessionProvider;
21
+ protected RequirePassword: boolean;
22
+ protected ProtectedRoles: string[];
23
+ /**
24
+ * Get impersonation state
25
+ * Returns whether an impersonation is currently active for this session.
26
+ * @security cookieAuth
27
+ * @returns {IImpersonationState}
28
+ * @response 401 No active session
29
+ */
30
+ getState(Impersonator: string, User: string, ImpersonationStartedAt: string): Promise<Ok<IImpersonationState>>;
31
+ /**
32
+ * Start impersonation
33
+ * Begins impersonating the target user. The caller must have `createAny` on
34
+ * virtual resource `user:impersonate`. The target must not hold any role in
35
+ * `rbac.impersonation.protectedRoles` and must not have effective grants
36
+ * exceeding the caller's. If `rbac.impersonation.requirePassword` is true,
37
+ * the caller's password must be supplied and is verified.
38
+ * @security cookieAuth
39
+ * @returns {IImpersonationResponse}
40
+ * @response 400 Target equals caller or invalid payload
41
+ * @response 401 Password required or invalid
42
+ * @response 403 Caller lacks permission, target is protected, or escalation detected
43
+ * @response 404 Target user not found / inactive / banned / deleted
44
+ * @response 409 An impersonation is already in progress for this session
45
+ */
46
+ start(caller: User, session: ISession, payload: ImpersonateDto): Promise<Ok<IImpersonationResponse> | BadRequestResponse | Unauthorized | ForbiddenResponse | NotFound | Conflict>;
47
+ /**
48
+ * Stop impersonation
49
+ * Restores the original user's session and returns their login-style payload.
50
+ * @security cookieAuth
51
+ * @returns {IUserWithGrants}
52
+ * @response 400 No impersonation is currently active
53
+ */
54
+ stop(target: User, session: ISession): Promise<Ok<IUserWithGrants> | BadRequestResponse>;
55
+ /**
56
+ * Emit an impersonation lifecycle event. Wrapped in a protected method so
57
+ * tests can intercept without stubbing module-level ESM bindings.
58
+ */
59
+ protected emitEvent(event: UserImpersonationStarted | UserImpersonationEnded): Promise<void>;
60
+ /**
61
+ * Load the impersonation target (with Metadata so IsBanned works). Extracted
62
+ * as a protected method so tests can stub it without setting up a database.
63
+ */
64
+ protected loadTarget(uuid: string): Promise<User | undefined>;
65
+ /**
66
+ * Load the original (impersonator) user. Extracted for the same reason as
67
+ * loadTarget — keeps the controller easy to test in isolation.
68
+ */
69
+ protected loadOriginal(uuid: string): Promise<User | undefined>;
70
+ protected buildResponse(target: User, impersonator: User, activeRole: string, startedAt: string): IImpersonationResponse;
71
+ }
72
+ //# sourceMappingURL=ImpersonationController.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImpersonationController.d.ts","sourceRoot":"","sources":["../../../src/controllers/ImpersonationController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAA6B,EAAE,EAAO,kBAAkB,EAAE,YAAY,EAAE,iBAAiB,EAAE,QAAQ,EAAE,QAAQ,EAAU,MAAM,eAAe,CAAC;AACpK,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,IAAI,EACJ,wBAAwB,EACxB,sBAAsB,EAGvB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAK9C,OAAO,EAKL,sBAAsB,EACtB,mBAAmB,EACnB,eAAe,EAChB,MAAM,oBAAoB,CAAC;AAI5B;;;;;;;;;;GAUG;AACH,qBACa,uBAAwB,SAAQ,cAAc;IAEzD,SAAS,CAAC,EAAE,EAAE,aAAa,CAAC;IAG5B,SAAS,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAG7C,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC;IAG3C,SAAS,CAAC,eAAe,EAAE,OAAO,CAAC;IAGnC,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;IAEnC;;;;;;OAMG;IAGU,QAAQ,CACJ,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,EACZ,sBAAsB,EAAE,MAAM,GAC5C,OAAO,CAAC,EAAE,CAAC,mBAAmB,CAAC,CAAC;IAYnC;;;;;;;;;;;;;;OAcG;IAGU,KAAK,CACA,MAAM,EAAE,IAAI,EACT,OAAO,EAAE,QAAQ,EAC5B,OAAO,EAAE,cAAc,GAC9B,OAAO,CACR,EAAE,CAAC,sBAAsB,CAAC,GAAG,kBAAkB,GAAG,YAAY,GAAG,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,CACzG;IAsFD;;;;;;OAMG;IAGU,IAAI,CACC,MAAM,EAAE,IAAI,EACT,OAAO,EAAE,QAAQ,GACnC,OAAO,CAAC,EAAE,CAAC,eAAe,CAAC,GAAG,kBAAkB,CAAC;IA4CpD;;;OAGG;IACH,SAAS,CAAC,SAAS,CAAC,KAAK,EAAE,wBAAwB,GAAG,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5F;;;OAGG;IACH,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC;IAI7D;;;OAGG;IACH,SAAS,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC;IAI/D,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,sBAAsB;CAWzH"}
@@ -0,0 +1,274 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { ImpersonateDto } from '../dto/impersonate-dto.js';
14
+ import { BaseController, BasePath, Post, Del, Body, Ok, Get, BadRequestResponse, Unauthorized, ForbiddenResponse, Conflict, NotFound, Policy } from '@spinajs/http';
15
+ import { AccessControl, PasswordProvider, SessionProvider, User, UserImpersonationStarted, UserImpersonationEnded, _unwindGrants, canImpersonate, } from '@spinajs/rbac';
16
+ import { Autoinject } from '@spinajs/di';
17
+ import { AutoinjectService, Config } from '@spinajs/configuration';
18
+ import { _ev } from '@spinajs/queue';
19
+ import { DateTime } from 'luxon';
20
+ import { LoggedPolicy, User as UserRouteArg, Session as SessionRouteArg, FromSession, } from '@spinajs/rbac-http';
21
+ const IMPERSONATE_RESOURCE = 'user:impersonate';
22
+ /**
23
+ * Impersonation endpoints.
24
+ *
25
+ * A user holding `createAny` on the virtual resource `user:impersonate` can
26
+ * temporarily act as another user. While impersonation is active, the session
27
+ * carries both identities — `User` is the target, `Impersonator` is the
28
+ * original. Permission checks therefore "see" the target by default; the
29
+ * original is preserved only for audit and for ending the impersonation.
30
+ *
31
+ * @tags Authentication
32
+ */
33
+ let ImpersonationController = class ImpersonationController extends BaseController {
34
+ /**
35
+ * Get impersonation state
36
+ * Returns whether an impersonation is currently active for this session.
37
+ * @security cookieAuth
38
+ * @returns {IImpersonationState}
39
+ * @response 401 No active session
40
+ */
41
+ async getState(Impersonator, User, ImpersonationStartedAt) {
42
+ if (!Impersonator) {
43
+ return new Ok({ Active: false });
44
+ }
45
+ return new Ok({
46
+ Active: true,
47
+ ImpersonatorUuid: Impersonator,
48
+ TargetUuid: User,
49
+ StartedAt: ImpersonationStartedAt,
50
+ });
51
+ }
52
+ /**
53
+ * Start impersonation
54
+ * Begins impersonating the target user. The caller must have `createAny` on
55
+ * virtual resource `user:impersonate`. The target must not hold any role in
56
+ * `rbac.impersonation.protectedRoles` and must not have effective grants
57
+ * exceeding the caller's. If `rbac.impersonation.requirePassword` is true,
58
+ * the caller's password must be supplied and is verified.
59
+ * @security cookieAuth
60
+ * @returns {IImpersonationResponse}
61
+ * @response 400 Target equals caller or invalid payload
62
+ * @response 401 Password required or invalid
63
+ * @response 403 Caller lacks permission, target is protected, or escalation detected
64
+ * @response 404 Target user not found / inactive / banned / deleted
65
+ * @response 409 An impersonation is already in progress for this session
66
+ */
67
+ async start(caller, session, payload) {
68
+ if (session?.Data.get('Impersonator')) {
69
+ return new Conflict({
70
+ error: {
71
+ code: 'E_IMPERSONATION_ACTIVE',
72
+ message: 'An impersonation is already in progress. Stop the current one before starting another.',
73
+ },
74
+ });
75
+ }
76
+ if (caller.Uuid === payload.TargetUuid) {
77
+ return new BadRequestResponse({
78
+ error: { code: 'E_SELF_IMPERSONATION', message: 'Cannot impersonate yourself' },
79
+ });
80
+ }
81
+ // Permission to impersonate is itself an RBAC permission honoring ActiveRole.
82
+ const activeRole = session?.Data.get('ActiveRole') ?? caller.Role?.[0];
83
+ const roles = activeRole ? [activeRole] : caller.Role;
84
+ const allowed = this.AC.can(roles).createAny(IMPERSONATE_RESOURCE).granted;
85
+ if (!allowed) {
86
+ return new ForbiddenResponse({
87
+ error: { code: 'E_IMPERSONATE_FORBIDDEN', message: `Role(s) ${roles} cannot impersonate other users` },
88
+ });
89
+ }
90
+ const target = await this.loadTarget(payload.TargetUuid);
91
+ if (!target) {
92
+ return new NotFound({ error: { code: 'E_TARGET_NOT_FOUND', message: 'Target user not found' } });
93
+ }
94
+ if (!target.IsActive || target.IsBanned) {
95
+ return new NotFound({ error: { code: 'E_TARGET_UNAVAILABLE', message: 'Target user is not available' } });
96
+ }
97
+ const check = canImpersonate({
98
+ originalRoles: caller.Role,
99
+ targetRoles: target.Role,
100
+ protectedRoles: this.ProtectedRoles ?? [],
101
+ ac: this.AC,
102
+ });
103
+ if (!check.allowed) {
104
+ return new ForbiddenResponse({
105
+ error: {
106
+ code: check.reason === 'PROTECTED_ROLE' ? 'E_TARGET_PROTECTED' : 'E_PRIVILEGE_ESCALATION',
107
+ message: check.reason === 'PROTECTED_ROLE'
108
+ ? `Target has a protected role (${check.detail}) and cannot be impersonated`
109
+ : `Target has a privilege the impersonator lacks (${check.detail})`,
110
+ },
111
+ });
112
+ }
113
+ if (this.RequirePassword) {
114
+ if (!payload.Password) {
115
+ return new Unauthorized({
116
+ error: { code: 'E_PASSWORD_REQUIRED', message: 'Password confirmation is required to start impersonation' },
117
+ });
118
+ }
119
+ const valid = await this.PasswordProvider.verify(caller.Password, payload.Password);
120
+ if (!valid) {
121
+ return new Unauthorized({ error: { code: 'E_PASSWORD_INVALID', message: 'Invalid password' } });
122
+ }
123
+ }
124
+ // Persist impersonation state. We keep the impersonator's previous
125
+ // ActiveRole so it can be restored on stop; effective ActiveRole becomes
126
+ // the target's first role.
127
+ const startedAt = DateTime.now().toISO();
128
+ const previousActiveRole = session.Data.get('ActiveRole');
129
+ session.Data.set('Impersonator', caller.Uuid);
130
+ session.Data.set('User', target.Uuid);
131
+ session.Data.set('ImpersonationStartedAt', startedAt);
132
+ if (previousActiveRole !== undefined) {
133
+ session.Data.set('OriginalActiveRole', previousActiveRole);
134
+ }
135
+ const targetActiveRole = target.Role?.[0];
136
+ if (targetActiveRole) {
137
+ session.Data.set('ActiveRole', targetActiveRole);
138
+ }
139
+ await this.SessionProvider.save(session);
140
+ await this.emitEvent(new UserImpersonationStarted(caller, target));
141
+ return new Ok(this.buildResponse(target, caller, targetActiveRole, startedAt));
142
+ }
143
+ /**
144
+ * Stop impersonation
145
+ * Restores the original user's session and returns their login-style payload.
146
+ * @security cookieAuth
147
+ * @returns {IUserWithGrants}
148
+ * @response 400 No impersonation is currently active
149
+ */
150
+ async stop(target, session) {
151
+ const impersonatorUuid = session?.Data.get('Impersonator');
152
+ if (!impersonatorUuid) {
153
+ return new BadRequestResponse({
154
+ error: { code: 'E_NO_IMPERSONATION', message: 'No impersonation is currently in progress' },
155
+ });
156
+ }
157
+ const original = await this.loadOriginal(impersonatorUuid);
158
+ if (!original) {
159
+ // Stale session referencing a deleted impersonator — destroy the
160
+ // impersonation block to recover but report an error so the caller
161
+ // can re-authenticate.
162
+ session.Data.delete('Impersonator');
163
+ session.Data.delete('ImpersonationStartedAt');
164
+ session.Data.delete('OriginalActiveRole');
165
+ await this.SessionProvider.save(session);
166
+ return new BadRequestResponse({
167
+ error: { code: 'E_IMPERSONATOR_GONE', message: 'Original user no longer exists' },
168
+ });
169
+ }
170
+ // Restore the original session state.
171
+ session.Data.set('User', original.Uuid);
172
+ session.Data.delete('Impersonator');
173
+ session.Data.delete('ImpersonationStartedAt');
174
+ const restoredActiveRole = session.Data.get('OriginalActiveRole') ?? original.Role?.[0];
175
+ if (restoredActiveRole) {
176
+ session.Data.set('ActiveRole', restoredActiveRole);
177
+ }
178
+ session.Data.delete('OriginalActiveRole');
179
+ await this.SessionProvider.save(session);
180
+ await this.emitEvent(new UserImpersonationEnded(original, target));
181
+ const grants = restoredActiveRole ? _unwindGrants(restoredActiveRole, this.AC.getGrants()) : {};
182
+ return new Ok({
183
+ ...original.dehydrateWithRelations({ dateTimeFormat: 'iso' }),
184
+ ActiveRole: restoredActiveRole,
185
+ Grants: grants,
186
+ });
187
+ }
188
+ /**
189
+ * Emit an impersonation lifecycle event. Wrapped in a protected method so
190
+ * tests can intercept without stubbing module-level ESM bindings.
191
+ */
192
+ emitEvent(event) {
193
+ return _ev(event)();
194
+ }
195
+ /**
196
+ * Load the impersonation target (with Metadata so IsBanned works). Extracted
197
+ * as a protected method so tests can stub it without setting up a database.
198
+ */
199
+ loadTarget(uuid) {
200
+ return User.query().whereUuid(uuid).populate('Metadata').notDeleted().first();
201
+ }
202
+ /**
203
+ * Load the original (impersonator) user. Extracted for the same reason as
204
+ * loadTarget — keeps the controller easy to test in isolation.
205
+ */
206
+ loadOriginal(uuid) {
207
+ return User.getByUuid(uuid);
208
+ }
209
+ buildResponse(target, impersonator, activeRole, startedAt) {
210
+ const grants = activeRole ? _unwindGrants(activeRole, this.AC.getGrants()) : {};
211
+ return {
212
+ User: target.dehydrateWithRelations({ dateTimeFormat: 'iso' }),
213
+ Impersonator: impersonator.dehydrateWithRelations({ dateTimeFormat: 'iso' }),
214
+ ActiveRole: activeRole,
215
+ AvailableRoles: target.Role ?? [],
216
+ Grants: grants,
217
+ StartedAt: startedAt,
218
+ };
219
+ }
220
+ };
221
+ __decorate([
222
+ Autoinject(AccessControl),
223
+ __metadata("design:type", AccessControl)
224
+ ], ImpersonationController.prototype, "AC", void 0);
225
+ __decorate([
226
+ AutoinjectService('rbac.password'),
227
+ __metadata("design:type", PasswordProvider)
228
+ ], ImpersonationController.prototype, "PasswordProvider", void 0);
229
+ __decorate([
230
+ AutoinjectService('rbac.session'),
231
+ __metadata("design:type", SessionProvider)
232
+ ], ImpersonationController.prototype, "SessionProvider", void 0);
233
+ __decorate([
234
+ Config('rbac.impersonation.requirePassword', { defaultValue: true }),
235
+ __metadata("design:type", Boolean)
236
+ ], ImpersonationController.prototype, "RequirePassword", void 0);
237
+ __decorate([
238
+ Config('rbac.impersonation.protectedRoles', { defaultValue: ['system'] }),
239
+ __metadata("design:type", Array)
240
+ ], ImpersonationController.prototype, "ProtectedRoles", void 0);
241
+ __decorate([
242
+ Get('impersonate'),
243
+ Policy(LoggedPolicy),
244
+ __param(0, FromSession()),
245
+ __param(1, FromSession()),
246
+ __param(2, FromSession()),
247
+ __metadata("design:type", Function),
248
+ __metadata("design:paramtypes", [String, String, String]),
249
+ __metadata("design:returntype", Promise)
250
+ ], ImpersonationController.prototype, "getState", null);
251
+ __decorate([
252
+ Post('impersonate'),
253
+ Policy(LoggedPolicy),
254
+ __param(0, UserRouteArg()),
255
+ __param(1, SessionRouteArg()),
256
+ __param(2, Body()),
257
+ __metadata("design:type", Function),
258
+ __metadata("design:paramtypes", [User, Object, ImpersonateDto]),
259
+ __metadata("design:returntype", Promise)
260
+ ], ImpersonationController.prototype, "start", null);
261
+ __decorate([
262
+ Del('impersonate'),
263
+ Policy(LoggedPolicy),
264
+ __param(0, UserRouteArg()),
265
+ __param(1, SessionRouteArg()),
266
+ __metadata("design:type", Function),
267
+ __metadata("design:paramtypes", [User, Object]),
268
+ __metadata("design:returntype", Promise)
269
+ ], ImpersonationController.prototype, "stop", null);
270
+ ImpersonationController = __decorate([
271
+ BasePath('auth')
272
+ ], ImpersonationController);
273
+ export { ImpersonationController };
274
+ //# sourceMappingURL=ImpersonationController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImpersonationController.js","sourceRoot":"","sources":["../../../src/controllers/ImpersonationController.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@ import { AuthProvider, SessionProvider, AccessControl } from '@spinajs/rbac';
4
4
  import { Configuration } from '@spinajs/configuration';
5
5
  import { ILoginResponse } from '@spinajs/rbac-http';
6
6
  import { User } from '@spinajs/rbac';
7
+ import type { ISession } from '@spinajs/rbac';
7
8
  /**
8
9
  * Authentication endpoints.
9
10
  * Handles user login, logout, and current-session inspection.
@@ -33,19 +34,41 @@ export declare class LoginController extends BaseController {
33
34
  /**
34
35
  * Logout
35
36
  * Destroys the current session identified by the `ssid` cookie and clears the cookie on the client.
37
+ * If an impersonation is active, the session is NOT destroyed — instead the
38
+ * impersonation is ended and the original user resumes their session.
36
39
  * Requires the user to be logged in (session exists), but full authorization (2FA) is not required.
37
40
  * @security cookieAuth
38
41
  * @response 401 No active session
39
42
  */
40
- logout(ssid: string): Promise<Ok<any>>;
43
+ logout(ssid: string, session: ISession, user: User): Promise<Ok<any>>;
41
44
  /**
42
45
  * Get current user
43
- * Returns the user object associated with the current session.
46
+ * Returns the user object associated with the current session along with the
47
+ * currently active role and the full list of roles the user may switch to.
44
48
  * Requires the user to be logged in (session exists), but full authorization (2FA) is not required.
45
49
  * @security cookieAuth
46
- * @returns {IUserProfile} User data from the current session
50
+ * @returns {User} User data from the current session
47
51
  * @response 401 No active session
48
52
  */
49
- whoami(User: User): Promise<Ok<User>>;
53
+ whoami(User: User, ActiveRole: string): Promise<Ok<{
54
+ ActiveRole: string;
55
+ AvailableRoles: string[];
56
+ Email: string;
57
+ Password: string;
58
+ Id: number;
59
+ Uuid: string;
60
+ Login: string;
61
+ Role: string[];
62
+ CreatedAt: import("luxon").DateTime;
63
+ RegisteredAt: import("luxon").DateTime;
64
+ DeletedAt: import("luxon").DateTime;
65
+ LastLoginAt: import("luxon").DateTime;
66
+ IsActive: boolean;
67
+ IsGuest: boolean;
68
+ IsBanned: boolean;
69
+ IsDirty: boolean;
70
+ Metadata: Omit<import("@spinajs/orm").ModelDataWithRelationData<import("@spinajs/rbac").UserMetadataBase>, import("@spinajs/orm").ExcludedModelProperties>[];
71
+ PrimaryKeyValue: any;
72
+ }>>;
50
73
  }
51
74
  //# sourceMappingURL=LoginController.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LoginController.d.ts","sourceRoot":"","sources":["../../../src/controllers/LoginController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAwB,EAAE,EAAe,YAAY,EAAU,MAAM,eAAe,CAAC;AAC5G,OAAO,EAAE,YAAY,EAAE,eAAe,EAAsB,aAAa,EAAiB,MAAM,eAAe,CAAC;AAEhH,OAAO,EAA6B,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAElF,OAAO,EAAsC,cAAc,EAAmB,MAAM,oBAAoB,CAAC;AACzG,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAGrC;;;;;GAKG;AACH,qBACa,eAAgB,SAAQ,cAAc;IAEjD,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IAGvC,SAAS,CAAC,YAAY,EAAE,YAAY,CAAC;IAGrC,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC;IAK3C,SAAS,CAAC,qBAAqB,EAAE,MAAM,CAAC;IAKxC,SAAS,CAAC,oBAAoB,EAAE,OAAO,CAAC;IAMxC,SAAS,CAAC,sBAAsB,EAAE,OAAO,CAAC;IAG1C,SAAS,CAAC,mBAAmB,EAAE,GAAG,CAAC;IAGnC,SAAS,CAAC,EAAE,EAAE,aAAa,CAAC;IAE5B;;;;;;;;;OASG;IAEU,KAAK,CAAiB,MAAM,EAAE,IAAI,EAAgB,IAAI,EAAE,MAAM,EAAU,WAAW,EAAE,YAAY,GAAG,OAAO,CAAC,EAAE,CAAC,cAAc,CAAC,GAAG,YAAY,CAAC;IAwG3J;;;;;;OAMG;IAGU,MAAM,CAAe,IAAI,EAAE,MAAM;IA0B9C;;;;;;;OAOG;IAGU,MAAM,CAAiB,IAAI,EAAE,IAAI;CAK/C"}
1
+ {"version":3,"file":"LoginController.d.ts","sourceRoot":"","sources":["../../../src/controllers/LoginController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAwB,EAAE,EAAe,YAAY,EAAU,MAAM,eAAe,CAAC;AAC5G,OAAO,EAAE,YAAY,EAAE,eAAe,EAAsB,aAAa,EAAiB,MAAM,eAAe,CAAC;AAEhH,OAAO,EAA6B,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAElF,OAAO,EAA+E,cAAc,EAAmB,MAAM,oBAAoB,CAAC;AAClJ,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAI9C;;;;;GAKG;AACH,qBACa,eAAgB,SAAQ,cAAc;IAEjD,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IAGvC,SAAS,CAAC,YAAY,EAAE,YAAY,CAAC;IAGrC,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC;IAK3C,SAAS,CAAC,qBAAqB,EAAE,MAAM,CAAC;IAKxC,SAAS,CAAC,oBAAoB,EAAE,OAAO,CAAC;IAMxC,SAAS,CAAC,sBAAsB,EAAE,OAAO,CAAC;IAG1C,SAAS,CAAC,mBAAmB,EAAE,GAAG,CAAC;IAGnC,SAAS,CAAC,EAAE,EAAE,aAAa,CAAC;IAE5B;;;;;;;;;OASG;IAEU,KAAK,CAAiB,MAAM,EAAE,IAAI,EAAgB,IAAI,EAAE,MAAM,EAAU,WAAW,EAAE,YAAY,GAAG,OAAO,CAAC,EAAE,CAAC,cAAc,CAAC,GAAG,YAAY,CAAC;IA+G3J;;;;;;;;OAQG;IAGU,MAAM,CAAe,IAAI,EAAE,MAAM,EAAqB,OAAO,EAAE,QAAQ,EAAkB,IAAI,EAAE,IAAI;IA6BhH;;;;;;;;OAQG;IAGU,MAAM,CAAiB,IAAI,EAAE,IAAI,EAAiB,UAAU,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;CAQlF"}