@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,135 @@
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
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.ActiveRoleController = void 0;
16
+ const switchRole_dto_js_1 = require("../dto/switchRole-dto.js");
17
+ const http_1 = require("@spinajs/http");
18
+ const rbac_1 = require("@spinajs/rbac");
19
+ const di_1 = require("@spinajs/di");
20
+ const configuration_1 = require("@spinajs/configuration");
21
+ const rbac_http_1 = require("@spinajs/rbac-http");
22
+ /**
23
+ * Active role endpoints.
24
+ * Let users with multiple roles inspect and switch the currently active role.
25
+ * The active role drives all request-bound permission checks; the user's full
26
+ * role list remains available so they can switch back at any time.
27
+ * @tags Authentication
28
+ */
29
+ let ActiveRoleController = class ActiveRoleController extends http_1.BaseController {
30
+ /**
31
+ * Get active role
32
+ * Returns the currently active role for the session, all roles the user may switch to,
33
+ * and the RBAC grants resolved for the active role.
34
+ * @security cookieAuth
35
+ * @returns {IActiveRoleResponse}
36
+ * @response 401 No active session
37
+ */
38
+ async getActiveRole(user, ActiveRole) {
39
+ return new http_1.Ok(this.buildResponse(user, ActiveRole ?? user.Role?.[0]));
40
+ }
41
+ /**
42
+ * Switch active role
43
+ * Switches the active role for the current session. The requested role must be one of
44
+ * the user's assigned roles. If the role is listed in `rbac.roleSwitch.requirePassword`,
45
+ * the user's password must also be provided and is re-verified.
46
+ * @security cookieAuth
47
+ * @returns {IActiveRoleResponse}
48
+ * @response 400 Requested role is not assigned to the user
49
+ * @response 401 Password verification required or failed
50
+ */
51
+ async switchActiveRole(user, session, payload) {
52
+ if (!user.Role?.includes(payload.Role)) {
53
+ return new http_1.BadRequestResponse({
54
+ error: {
55
+ code: 'E_ROLE_NOT_ASSIGNED',
56
+ message: `Role '${payload.Role}' is not assigned to user`,
57
+ },
58
+ });
59
+ }
60
+ if (this.RolesRequiringPassword?.includes(payload.Role)) {
61
+ if (!payload.Password) {
62
+ return new http_1.Unauthorized({
63
+ error: {
64
+ code: 'E_PASSWORD_REQUIRED',
65
+ message: `Password is required to activate role '${payload.Role}'`,
66
+ },
67
+ });
68
+ }
69
+ const valid = await this.PasswordProvider.verify(user.Password, payload.Password);
70
+ if (!valid) {
71
+ return new http_1.Unauthorized({
72
+ error: {
73
+ code: 'E_PASSWORD_INVALID',
74
+ message: 'Invalid password',
75
+ },
76
+ });
77
+ }
78
+ }
79
+ session.Data.set('ActiveRole', payload.Role);
80
+ await this.SessionProvider.save(session);
81
+ return new http_1.Ok(this.buildResponse(user, payload.Role));
82
+ }
83
+ buildResponse(user, activeRole) {
84
+ const grants = activeRole ? (0, rbac_1._unwindGrants)(activeRole, this.AC.getGrants()) : {};
85
+ return {
86
+ ActiveRole: activeRole,
87
+ AvailableRoles: user.Role ?? [],
88
+ Grants: grants,
89
+ };
90
+ }
91
+ };
92
+ exports.ActiveRoleController = ActiveRoleController;
93
+ __decorate([
94
+ (0, di_1.Autoinject)(rbac_1.AccessControl),
95
+ __metadata("design:type", rbac_1.AccessControl)
96
+ ], ActiveRoleController.prototype, "AC", void 0);
97
+ __decorate([
98
+ (0, configuration_1.AutoinjectService)('rbac.auth'),
99
+ __metadata("design:type", rbac_1.AuthProvider)
100
+ ], ActiveRoleController.prototype, "AuthProvider", void 0);
101
+ __decorate([
102
+ (0, configuration_1.AutoinjectService)('rbac.password'),
103
+ __metadata("design:type", rbac_1.PasswordProvider)
104
+ ], ActiveRoleController.prototype, "PasswordProvider", void 0);
105
+ __decorate([
106
+ (0, configuration_1.AutoinjectService)('rbac.session'),
107
+ __metadata("design:type", rbac_1.SessionProvider)
108
+ ], ActiveRoleController.prototype, "SessionProvider", void 0);
109
+ __decorate([
110
+ (0, configuration_1.Config)('rbac.roleSwitch.requirePassword', { defaultValue: [] }),
111
+ __metadata("design:type", Array)
112
+ ], ActiveRoleController.prototype, "RolesRequiringPassword", void 0);
113
+ __decorate([
114
+ (0, http_1.Get)('active-role'),
115
+ (0, http_1.Policy)(rbac_http_1.LoggedPolicy),
116
+ __param(0, (0, rbac_http_1.User)()),
117
+ __param(1, (0, rbac_http_1.FromSession)()),
118
+ __metadata("design:type", Function),
119
+ __metadata("design:paramtypes", [Function, String]),
120
+ __metadata("design:returntype", Promise)
121
+ ], ActiveRoleController.prototype, "getActiveRole", null);
122
+ __decorate([
123
+ (0, http_1.Post)('active-role'),
124
+ (0, http_1.Policy)(rbac_http_1.LoggedPolicy),
125
+ __param(0, (0, rbac_http_1.User)()),
126
+ __param(1, (0, rbac_http_1.Session)()),
127
+ __param(2, (0, http_1.Body)()),
128
+ __metadata("design:type", Function),
129
+ __metadata("design:paramtypes", [Function, Object, switchRole_dto_js_1.SwitchRoleDto]),
130
+ __metadata("design:returntype", Promise)
131
+ ], ActiveRoleController.prototype, "switchActiveRole", null);
132
+ exports.ActiveRoleController = ActiveRoleController = __decorate([
133
+ (0, http_1.BasePath)('auth')
134
+ ], ActiveRoleController);
135
+ //# sourceMappingURL=ActiveRoleController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActiveRoleController.js","sourceRoot":"","sources":["../../../src/controllers/ActiveRoleController.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,gEAAyD;AACzD,wCAAwH;AACxH,wCAA8G;AAE9G,oCAAyC;AACzC,0DAAmE;AACnE,kDAAsI;AAEtI;;;;;;GAMG;AAEI,IAAM,oBAAoB,GAA1B,MAAM,oBAAqB,SAAQ,qBAAc;IAgBtD;;;;;;;OAOG;IAGU,AAAN,KAAK,CAAC,aAAa,CAAiB,IAAU,EAAiB,UAAkB;QACtF,OAAO,IAAI,SAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,UAAU,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;IAED;;;;;;;;;OASG;IAGU,AAAN,KAAK,CAAC,gBAAgB,CACX,IAAU,EACP,OAAiB,EAC5B,OAAsB;QAE9B,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACvC,OAAO,IAAI,yBAAkB,CAAC;gBAC5B,KAAK,EAAE;oBACL,IAAI,EAAE,qBAAqB;oBAC3B,OAAO,EAAE,SAAS,OAAO,CAAC,IAAI,2BAA2B;iBAC1D;aACF,CAAC,CAAC;QACL,CAAC;QAED,IAAI,IAAI,CAAC,sBAAsB,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACxD,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACtB,OAAO,IAAI,mBAAY,CAAC;oBACtB,KAAK,EAAE;wBACL,IAAI,EAAE,qBAAqB;wBAC3B,OAAO,EAAE,0CAA0C,OAAO,CAAC,IAAI,GAAG;qBACnE;iBACF,CAAC,CAAC;YACL,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;YAClF,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,IAAI,mBAAY,CAAC;oBACtB,KAAK,EAAE;wBACL,IAAI,EAAE,oBAAoB;wBAC1B,OAAO,EAAE,kBAAkB;qBAC5B;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEzC,OAAO,IAAI,SAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACxD,CAAC;IAES,aAAa,CAAC,IAAU,EAAE,UAAkB;QACpD,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,IAAA,oBAAa,EAAC,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChF,OAAO;YACL,UAAU,EAAE,UAAU;YACtB,cAAc,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE;YAC/B,MAAM,EAAE,MAAM;SACf,CAAC;IACJ,CAAC;CACF,CAAA;AA3FY,oDAAoB;AAErB;IADT,IAAA,eAAU,EAAC,oBAAa,CAAC;8BACZ,oBAAa;gDAAC;AAGlB;IADT,IAAA,iCAAiB,EAAC,WAAW,CAAC;8BACP,mBAAY;0DAAC;AAG3B;IADT,IAAA,iCAAiB,EAAC,eAAe,CAAC;8BACP,uBAAgB;8DAAC;AAGnC;IADT,IAAA,iCAAiB,EAAC,cAAc,CAAC;8BACP,sBAAe;6DAAC;AAGjC;IADT,IAAA,sBAAM,EAAC,iCAAiC,EAAE,EAAE,YAAY,EAAE,EAAc,EAAE,CAAC;;oEACjC;AAY9B;IAFZ,IAAA,UAAG,EAAC,aAAa,CAAC;IAClB,IAAA,aAAM,EAAC,wBAAY,CAAC;IACO,WAAA,IAAA,gBAAY,GAAE,CAAA;IAAc,WAAA,IAAA,uBAAW,GAAE,CAAA;;;;yDAEpE;AAcY;IAFZ,IAAA,WAAI,EAAC,aAAa,CAAC;IACnB,IAAA,aAAM,EAAC,wBAAY,CAAC;IAElB,WAAA,IAAA,gBAAY,GAAE,CAAA;IACd,WAAA,IAAA,mBAAe,GAAE,CAAA;IACjB,WAAA,IAAA,WAAI,GAAE,CAAA;;uDAAU,iCAAa;;4DAoC/B;+BAjFU,oBAAoB;IADhC,IAAA,eAAQ,EAAC,MAAM,CAAC;GACJ,oBAAoB,CA2FhC"}
@@ -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,277 @@
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
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.ImpersonationController = void 0;
16
+ const impersonate_dto_js_1 = require("../dto/impersonate-dto.js");
17
+ const http_1 = require("@spinajs/http");
18
+ const rbac_1 = require("@spinajs/rbac");
19
+ const di_1 = require("@spinajs/di");
20
+ const configuration_1 = require("@spinajs/configuration");
21
+ const queue_1 = require("@spinajs/queue");
22
+ const luxon_1 = require("luxon");
23
+ const rbac_http_1 = require("@spinajs/rbac-http");
24
+ const IMPERSONATE_RESOURCE = 'user:impersonate';
25
+ /**
26
+ * Impersonation endpoints.
27
+ *
28
+ * A user holding `createAny` on the virtual resource `user:impersonate` can
29
+ * temporarily act as another user. While impersonation is active, the session
30
+ * carries both identities — `User` is the target, `Impersonator` is the
31
+ * original. Permission checks therefore "see" the target by default; the
32
+ * original is preserved only for audit and for ending the impersonation.
33
+ *
34
+ * @tags Authentication
35
+ */
36
+ let ImpersonationController = class ImpersonationController extends http_1.BaseController {
37
+ /**
38
+ * Get impersonation state
39
+ * Returns whether an impersonation is currently active for this session.
40
+ * @security cookieAuth
41
+ * @returns {IImpersonationState}
42
+ * @response 401 No active session
43
+ */
44
+ async getState(Impersonator, User, ImpersonationStartedAt) {
45
+ if (!Impersonator) {
46
+ return new http_1.Ok({ Active: false });
47
+ }
48
+ return new http_1.Ok({
49
+ Active: true,
50
+ ImpersonatorUuid: Impersonator,
51
+ TargetUuid: User,
52
+ StartedAt: ImpersonationStartedAt,
53
+ });
54
+ }
55
+ /**
56
+ * Start impersonation
57
+ * Begins impersonating the target user. The caller must have `createAny` on
58
+ * virtual resource `user:impersonate`. The target must not hold any role in
59
+ * `rbac.impersonation.protectedRoles` and must not have effective grants
60
+ * exceeding the caller's. If `rbac.impersonation.requirePassword` is true,
61
+ * the caller's password must be supplied and is verified.
62
+ * @security cookieAuth
63
+ * @returns {IImpersonationResponse}
64
+ * @response 400 Target equals caller or invalid payload
65
+ * @response 401 Password required or invalid
66
+ * @response 403 Caller lacks permission, target is protected, or escalation detected
67
+ * @response 404 Target user not found / inactive / banned / deleted
68
+ * @response 409 An impersonation is already in progress for this session
69
+ */
70
+ async start(caller, session, payload) {
71
+ if (session?.Data.get('Impersonator')) {
72
+ return new http_1.Conflict({
73
+ error: {
74
+ code: 'E_IMPERSONATION_ACTIVE',
75
+ message: 'An impersonation is already in progress. Stop the current one before starting another.',
76
+ },
77
+ });
78
+ }
79
+ if (caller.Uuid === payload.TargetUuid) {
80
+ return new http_1.BadRequestResponse({
81
+ error: { code: 'E_SELF_IMPERSONATION', message: 'Cannot impersonate yourself' },
82
+ });
83
+ }
84
+ // Permission to impersonate is itself an RBAC permission honoring ActiveRole.
85
+ const activeRole = session?.Data.get('ActiveRole') ?? caller.Role?.[0];
86
+ const roles = activeRole ? [activeRole] : caller.Role;
87
+ const allowed = this.AC.can(roles).createAny(IMPERSONATE_RESOURCE).granted;
88
+ if (!allowed) {
89
+ return new http_1.ForbiddenResponse({
90
+ error: { code: 'E_IMPERSONATE_FORBIDDEN', message: `Role(s) ${roles} cannot impersonate other users` },
91
+ });
92
+ }
93
+ const target = await this.loadTarget(payload.TargetUuid);
94
+ if (!target) {
95
+ return new http_1.NotFound({ error: { code: 'E_TARGET_NOT_FOUND', message: 'Target user not found' } });
96
+ }
97
+ if (!target.IsActive || target.IsBanned) {
98
+ return new http_1.NotFound({ error: { code: 'E_TARGET_UNAVAILABLE', message: 'Target user is not available' } });
99
+ }
100
+ const check = (0, rbac_1.canImpersonate)({
101
+ originalRoles: caller.Role,
102
+ targetRoles: target.Role,
103
+ protectedRoles: this.ProtectedRoles ?? [],
104
+ ac: this.AC,
105
+ });
106
+ if (!check.allowed) {
107
+ return new http_1.ForbiddenResponse({
108
+ error: {
109
+ code: check.reason === 'PROTECTED_ROLE' ? 'E_TARGET_PROTECTED' : 'E_PRIVILEGE_ESCALATION',
110
+ message: check.reason === 'PROTECTED_ROLE'
111
+ ? `Target has a protected role (${check.detail}) and cannot be impersonated`
112
+ : `Target has a privilege the impersonator lacks (${check.detail})`,
113
+ },
114
+ });
115
+ }
116
+ if (this.RequirePassword) {
117
+ if (!payload.Password) {
118
+ return new http_1.Unauthorized({
119
+ error: { code: 'E_PASSWORD_REQUIRED', message: 'Password confirmation is required to start impersonation' },
120
+ });
121
+ }
122
+ const valid = await this.PasswordProvider.verify(caller.Password, payload.Password);
123
+ if (!valid) {
124
+ return new http_1.Unauthorized({ error: { code: 'E_PASSWORD_INVALID', message: 'Invalid password' } });
125
+ }
126
+ }
127
+ // Persist impersonation state. We keep the impersonator's previous
128
+ // ActiveRole so it can be restored on stop; effective ActiveRole becomes
129
+ // the target's first role.
130
+ const startedAt = luxon_1.DateTime.now().toISO();
131
+ const previousActiveRole = session.Data.get('ActiveRole');
132
+ session.Data.set('Impersonator', caller.Uuid);
133
+ session.Data.set('User', target.Uuid);
134
+ session.Data.set('ImpersonationStartedAt', startedAt);
135
+ if (previousActiveRole !== undefined) {
136
+ session.Data.set('OriginalActiveRole', previousActiveRole);
137
+ }
138
+ const targetActiveRole = target.Role?.[0];
139
+ if (targetActiveRole) {
140
+ session.Data.set('ActiveRole', targetActiveRole);
141
+ }
142
+ await this.SessionProvider.save(session);
143
+ await this.emitEvent(new rbac_1.UserImpersonationStarted(caller, target));
144
+ return new http_1.Ok(this.buildResponse(target, caller, targetActiveRole, startedAt));
145
+ }
146
+ /**
147
+ * Stop impersonation
148
+ * Restores the original user's session and returns their login-style payload.
149
+ * @security cookieAuth
150
+ * @returns {IUserWithGrants}
151
+ * @response 400 No impersonation is currently active
152
+ */
153
+ async stop(target, session) {
154
+ const impersonatorUuid = session?.Data.get('Impersonator');
155
+ if (!impersonatorUuid) {
156
+ return new http_1.BadRequestResponse({
157
+ error: { code: 'E_NO_IMPERSONATION', message: 'No impersonation is currently in progress' },
158
+ });
159
+ }
160
+ const original = await this.loadOriginal(impersonatorUuid);
161
+ if (!original) {
162
+ // Stale session referencing a deleted impersonator — destroy the
163
+ // impersonation block to recover but report an error so the caller
164
+ // can re-authenticate.
165
+ session.Data.delete('Impersonator');
166
+ session.Data.delete('ImpersonationStartedAt');
167
+ session.Data.delete('OriginalActiveRole');
168
+ await this.SessionProvider.save(session);
169
+ return new http_1.BadRequestResponse({
170
+ error: { code: 'E_IMPERSONATOR_GONE', message: 'Original user no longer exists' },
171
+ });
172
+ }
173
+ // Restore the original session state.
174
+ session.Data.set('User', original.Uuid);
175
+ session.Data.delete('Impersonator');
176
+ session.Data.delete('ImpersonationStartedAt');
177
+ const restoredActiveRole = session.Data.get('OriginalActiveRole') ?? original.Role?.[0];
178
+ if (restoredActiveRole) {
179
+ session.Data.set('ActiveRole', restoredActiveRole);
180
+ }
181
+ session.Data.delete('OriginalActiveRole');
182
+ await this.SessionProvider.save(session);
183
+ await this.emitEvent(new rbac_1.UserImpersonationEnded(original, target));
184
+ const grants = restoredActiveRole ? (0, rbac_1._unwindGrants)(restoredActiveRole, this.AC.getGrants()) : {};
185
+ return new http_1.Ok({
186
+ ...original.dehydrateWithRelations({ dateTimeFormat: 'iso' }),
187
+ ActiveRole: restoredActiveRole,
188
+ Grants: grants,
189
+ });
190
+ }
191
+ /**
192
+ * Emit an impersonation lifecycle event. Wrapped in a protected method so
193
+ * tests can intercept without stubbing module-level ESM bindings.
194
+ */
195
+ emitEvent(event) {
196
+ return (0, queue_1._ev)(event)();
197
+ }
198
+ /**
199
+ * Load the impersonation target (with Metadata so IsBanned works). Extracted
200
+ * as a protected method so tests can stub it without setting up a database.
201
+ */
202
+ loadTarget(uuid) {
203
+ return rbac_1.User.query().whereUuid(uuid).populate('Metadata').notDeleted().first();
204
+ }
205
+ /**
206
+ * Load the original (impersonator) user. Extracted for the same reason as
207
+ * loadTarget — keeps the controller easy to test in isolation.
208
+ */
209
+ loadOriginal(uuid) {
210
+ return rbac_1.User.getByUuid(uuid);
211
+ }
212
+ buildResponse(target, impersonator, activeRole, startedAt) {
213
+ const grants = activeRole ? (0, rbac_1._unwindGrants)(activeRole, this.AC.getGrants()) : {};
214
+ return {
215
+ User: target.dehydrateWithRelations({ dateTimeFormat: 'iso' }),
216
+ Impersonator: impersonator.dehydrateWithRelations({ dateTimeFormat: 'iso' }),
217
+ ActiveRole: activeRole,
218
+ AvailableRoles: target.Role ?? [],
219
+ Grants: grants,
220
+ StartedAt: startedAt,
221
+ };
222
+ }
223
+ };
224
+ exports.ImpersonationController = ImpersonationController;
225
+ __decorate([
226
+ (0, di_1.Autoinject)(rbac_1.AccessControl),
227
+ __metadata("design:type", rbac_1.AccessControl)
228
+ ], ImpersonationController.prototype, "AC", void 0);
229
+ __decorate([
230
+ (0, configuration_1.AutoinjectService)('rbac.password'),
231
+ __metadata("design:type", rbac_1.PasswordProvider)
232
+ ], ImpersonationController.prototype, "PasswordProvider", void 0);
233
+ __decorate([
234
+ (0, configuration_1.AutoinjectService)('rbac.session'),
235
+ __metadata("design:type", rbac_1.SessionProvider)
236
+ ], ImpersonationController.prototype, "SessionProvider", void 0);
237
+ __decorate([
238
+ (0, configuration_1.Config)('rbac.impersonation.requirePassword', { defaultValue: true }),
239
+ __metadata("design:type", Boolean)
240
+ ], ImpersonationController.prototype, "RequirePassword", void 0);
241
+ __decorate([
242
+ (0, configuration_1.Config)('rbac.impersonation.protectedRoles', { defaultValue: ['system'] }),
243
+ __metadata("design:type", Array)
244
+ ], ImpersonationController.prototype, "ProtectedRoles", void 0);
245
+ __decorate([
246
+ (0, http_1.Get)('impersonate'),
247
+ (0, http_1.Policy)(rbac_http_1.LoggedPolicy),
248
+ __param(0, (0, rbac_http_1.FromSession)()),
249
+ __param(1, (0, rbac_http_1.FromSession)()),
250
+ __param(2, (0, rbac_http_1.FromSession)()),
251
+ __metadata("design:type", Function),
252
+ __metadata("design:paramtypes", [String, String, String]),
253
+ __metadata("design:returntype", Promise)
254
+ ], ImpersonationController.prototype, "getState", null);
255
+ __decorate([
256
+ (0, http_1.Post)('impersonate'),
257
+ (0, http_1.Policy)(rbac_http_1.LoggedPolicy),
258
+ __param(0, (0, rbac_http_1.User)()),
259
+ __param(1, (0, rbac_http_1.Session)()),
260
+ __param(2, (0, http_1.Body)()),
261
+ __metadata("design:type", Function),
262
+ __metadata("design:paramtypes", [rbac_1.User, Object, impersonate_dto_js_1.ImpersonateDto]),
263
+ __metadata("design:returntype", Promise)
264
+ ], ImpersonationController.prototype, "start", null);
265
+ __decorate([
266
+ (0, http_1.Del)('impersonate'),
267
+ (0, http_1.Policy)(rbac_http_1.LoggedPolicy),
268
+ __param(0, (0, rbac_http_1.User)()),
269
+ __param(1, (0, rbac_http_1.Session)()),
270
+ __metadata("design:type", Function),
271
+ __metadata("design:paramtypes", [rbac_1.User, Object]),
272
+ __metadata("design:returntype", Promise)
273
+ ], ImpersonationController.prototype, "stop", null);
274
+ exports.ImpersonationController = ImpersonationController = __decorate([
275
+ (0, http_1.BasePath)('auth')
276
+ ], ImpersonationController);
277
+ //# 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"}