@spinajs/rbac-http-user 2.0.486 → 2.0.488

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 (239) hide show
  1. package/lib/cjs/config/rbac-http.d.ts +0 -15
  2. package/lib/cjs/config/rbac-http.d.ts.map +1 -1
  3. package/lib/cjs/config/rbac-http.js +6 -16
  4. package/lib/cjs/config/rbac-http.js.map +1 -1
  5. package/lib/cjs/controllers/ActiveRoleController.d.ts +2 -1
  6. package/lib/cjs/controllers/ActiveRoleController.d.ts.map +1 -1
  7. package/lib/cjs/controllers/ActiveRoleController.js +7 -17
  8. package/lib/cjs/controllers/ActiveRoleController.js.map +1 -1
  9. package/lib/cjs/controllers/ImpersonationController.d.ts +7 -9
  10. package/lib/cjs/controllers/ImpersonationController.d.ts.map +1 -1
  11. package/lib/cjs/controllers/ImpersonationController.js +31 -56
  12. package/lib/cjs/controllers/ImpersonationController.js.map +1 -1
  13. package/lib/cjs/controllers/LoginController.d.ts +10 -3
  14. package/lib/cjs/controllers/LoginController.d.ts.map +1 -1
  15. package/lib/cjs/controllers/LoginController.js +39 -40
  16. package/lib/cjs/controllers/LoginController.js.map +1 -1
  17. package/lib/cjs/controllers/PasswordResetController.d.ts +54 -0
  18. package/lib/cjs/controllers/PasswordResetController.d.ts.map +1 -0
  19. package/lib/cjs/controllers/PasswordResetController.js +129 -0
  20. package/lib/cjs/controllers/PasswordResetController.js.map +1 -0
  21. package/lib/cjs/controllers/SessionsController.d.ts +65 -0
  22. package/lib/cjs/controllers/SessionsController.d.ts.map +1 -0
  23. package/lib/cjs/controllers/SessionsController.js +143 -0
  24. package/lib/cjs/controllers/SessionsController.js.map +1 -0
  25. package/lib/cjs/controllers/TwoFactorAuthController.d.ts +24 -23
  26. package/lib/cjs/controllers/TwoFactorAuthController.d.ts.map +1 -1
  27. package/lib/cjs/controllers/TwoFactorAuthController.js +39 -72
  28. package/lib/cjs/controllers/TwoFactorAuthController.js.map +1 -1
  29. package/lib/cjs/controllers/TwoFactorAuthUserController.d.ts +98 -0
  30. package/lib/cjs/controllers/TwoFactorAuthUserController.d.ts.map +1 -0
  31. package/lib/cjs/controllers/TwoFactorAuthUserController.js +195 -0
  32. package/lib/cjs/controllers/TwoFactorAuthUserController.js.map +1 -0
  33. package/lib/cjs/controllers/UserController.d.ts +10 -7
  34. package/lib/cjs/controllers/UserController.d.ts.map +1 -1
  35. package/lib/cjs/controllers/UserController.js +56 -58
  36. package/lib/cjs/controllers/UserController.js.map +1 -1
  37. package/lib/cjs/controllers/UserMetadataController.d.ts +6 -12
  38. package/lib/cjs/controllers/UserMetadataController.d.ts.map +1 -1
  39. package/lib/cjs/controllers/UserMetadataController.js +23 -72
  40. package/lib/cjs/controllers/UserMetadataController.js.map +1 -1
  41. package/lib/cjs/dto/confirm-password-dto.d.ts +23 -0
  42. package/lib/cjs/dto/confirm-password-dto.d.ts.map +1 -0
  43. package/lib/cjs/dto/{restore-password-dto.js → confirm-password-dto.js} +16 -12
  44. package/lib/cjs/dto/confirm-password-dto.js.map +1 -0
  45. package/lib/cjs/dto/password-reset-dto.d.ts +58 -0
  46. package/lib/cjs/dto/password-reset-dto.d.ts.map +1 -0
  47. package/lib/cjs/dto/password-reset-dto.js +57 -0
  48. package/lib/cjs/dto/password-reset-dto.js.map +1 -0
  49. package/lib/cjs/handlers/DefaultLogoutHandler.d.ts +4 -1
  50. package/lib/cjs/handlers/DefaultLogoutHandler.d.ts.map +1 -1
  51. package/lib/cjs/handlers/DefaultLogoutHandler.js +20 -13
  52. package/lib/cjs/handlers/DefaultLogoutHandler.js.map +1 -1
  53. package/lib/cjs/handlers/ImpersonationLogoutHandler.d.ts +4 -7
  54. package/lib/cjs/handlers/ImpersonationLogoutHandler.d.ts.map +1 -1
  55. package/lib/cjs/handlers/ImpersonationLogoutHandler.js +30 -31
  56. package/lib/cjs/handlers/ImpersonationLogoutHandler.js.map +1 -1
  57. package/lib/cjs/index.d.ts +20 -0
  58. package/lib/cjs/index.d.ts.map +1 -1
  59. package/lib/cjs/index.js +22 -0
  60. package/lib/cjs/index.js.map +1 -1
  61. package/lib/cjs/logout.d.ts +7 -0
  62. package/lib/cjs/logout.d.ts.map +1 -1
  63. package/lib/cjs/logout.js.map +1 -1
  64. package/lib/cjs/policies/2FaPolicy.d.ts +21 -1
  65. package/lib/cjs/policies/2FaPolicy.d.ts.map +1 -1
  66. package/lib/cjs/policies/2FaPolicy.js +31 -7
  67. package/lib/cjs/policies/2FaPolicy.js.map +1 -1
  68. package/lib/cjs/services/ImpersonationService.d.ts +90 -0
  69. package/lib/cjs/services/ImpersonationService.d.ts.map +1 -0
  70. package/lib/cjs/services/ImpersonationService.js +147 -0
  71. package/lib/cjs/services/ImpersonationService.js.map +1 -0
  72. package/lib/cjs/services/SessionCookies.d.ts +41 -0
  73. package/lib/cjs/services/SessionCookies.d.ts.map +1 -0
  74. package/lib/cjs/services/SessionCookies.js +62 -0
  75. package/lib/cjs/services/SessionCookies.js.map +1 -0
  76. package/lib/cjs/services/UserMetadataService.d.ts +53 -0
  77. package/lib/cjs/services/UserMetadataService.d.ts.map +1 -0
  78. package/lib/cjs/services/UserMetadataService.js +94 -0
  79. package/lib/cjs/services/UserMetadataService.js.map +1 -0
  80. package/lib/cjs/services/grants.d.ts +29 -0
  81. package/lib/cjs/services/grants.d.ts.map +1 -0
  82. package/lib/cjs/services/grants.js +43 -0
  83. package/lib/cjs/services/grants.js.map +1 -0
  84. package/lib/mjs/config/rbac-http.d.ts +0 -15
  85. package/lib/mjs/config/rbac-http.d.ts.map +1 -1
  86. package/lib/mjs/config/rbac-http.js +6 -16
  87. package/lib/mjs/config/rbac-http.js.map +1 -1
  88. package/lib/mjs/controllers/ActiveRoleController.d.ts +2 -1
  89. package/lib/mjs/controllers/ActiveRoleController.d.ts.map +1 -1
  90. package/lib/mjs/controllers/ActiveRoleController.js +8 -18
  91. package/lib/mjs/controllers/ActiveRoleController.js.map +1 -1
  92. package/lib/mjs/controllers/ImpersonationController.d.ts +7 -9
  93. package/lib/mjs/controllers/ImpersonationController.d.ts.map +1 -1
  94. package/lib/mjs/controllers/ImpersonationController.js +32 -57
  95. package/lib/mjs/controllers/ImpersonationController.js.map +1 -1
  96. package/lib/mjs/controllers/LoginController.d.ts +10 -3
  97. package/lib/mjs/controllers/LoginController.d.ts.map +1 -1
  98. package/lib/mjs/controllers/LoginController.js +42 -43
  99. package/lib/mjs/controllers/LoginController.js.map +1 -1
  100. package/lib/mjs/controllers/PasswordResetController.d.ts +54 -0
  101. package/lib/mjs/controllers/PasswordResetController.d.ts.map +1 -0
  102. package/lib/mjs/controllers/PasswordResetController.js +126 -0
  103. package/lib/mjs/controllers/PasswordResetController.js.map +1 -0
  104. package/lib/mjs/controllers/SessionsController.d.ts +65 -0
  105. package/lib/mjs/controllers/SessionsController.d.ts.map +1 -0
  106. package/lib/mjs/controllers/SessionsController.js +140 -0
  107. package/lib/mjs/controllers/SessionsController.js.map +1 -0
  108. package/lib/mjs/controllers/TwoFactorAuthController.d.ts +24 -23
  109. package/lib/mjs/controllers/TwoFactorAuthController.d.ts.map +1 -1
  110. package/lib/mjs/controllers/TwoFactorAuthController.js +43 -76
  111. package/lib/mjs/controllers/TwoFactorAuthController.js.map +1 -1
  112. package/lib/mjs/controllers/TwoFactorAuthUserController.d.ts +98 -0
  113. package/lib/mjs/controllers/TwoFactorAuthUserController.d.ts.map +1 -0
  114. package/lib/mjs/controllers/TwoFactorAuthUserController.js +192 -0
  115. package/lib/mjs/controllers/TwoFactorAuthUserController.js.map +1 -0
  116. package/lib/mjs/controllers/UserController.d.ts +10 -7
  117. package/lib/mjs/controllers/UserController.d.ts.map +1 -1
  118. package/lib/mjs/controllers/UserController.js +59 -28
  119. package/lib/mjs/controllers/UserController.js.map +1 -1
  120. package/lib/mjs/controllers/UserMetadataController.d.ts +6 -12
  121. package/lib/mjs/controllers/UserMetadataController.d.ts.map +1 -1
  122. package/lib/mjs/controllers/UserMetadataController.js +23 -72
  123. package/lib/mjs/controllers/UserMetadataController.js.map +1 -1
  124. package/lib/mjs/dto/confirm-password-dto.d.ts +23 -0
  125. package/lib/mjs/dto/confirm-password-dto.d.ts.map +1 -0
  126. package/lib/mjs/dto/{user-dto.js → confirm-password-dto.js} +15 -10
  127. package/lib/mjs/dto/confirm-password-dto.js.map +1 -0
  128. package/lib/mjs/dto/password-reset-dto.d.ts +58 -0
  129. package/lib/mjs/dto/password-reset-dto.d.ts.map +1 -0
  130. package/lib/mjs/dto/password-reset-dto.js +54 -0
  131. package/lib/mjs/dto/password-reset-dto.js.map +1 -0
  132. package/lib/mjs/handlers/DefaultLogoutHandler.d.ts +4 -1
  133. package/lib/mjs/handlers/DefaultLogoutHandler.d.ts.map +1 -1
  134. package/lib/mjs/handlers/DefaultLogoutHandler.js +23 -16
  135. package/lib/mjs/handlers/DefaultLogoutHandler.js.map +1 -1
  136. package/lib/mjs/handlers/ImpersonationLogoutHandler.d.ts +4 -7
  137. package/lib/mjs/handlers/ImpersonationLogoutHandler.d.ts.map +1 -1
  138. package/lib/mjs/handlers/ImpersonationLogoutHandler.js +31 -32
  139. package/lib/mjs/handlers/ImpersonationLogoutHandler.js.map +1 -1
  140. package/lib/mjs/index.d.ts +20 -0
  141. package/lib/mjs/index.d.ts.map +1 -1
  142. package/lib/mjs/index.js +22 -0
  143. package/lib/mjs/index.js.map +1 -1
  144. package/lib/mjs/logout.d.ts +7 -0
  145. package/lib/mjs/logout.d.ts.map +1 -1
  146. package/lib/mjs/logout.js.map +1 -1
  147. package/lib/mjs/policies/2FaPolicy.d.ts +21 -1
  148. package/lib/mjs/policies/2FaPolicy.d.ts.map +1 -1
  149. package/lib/mjs/policies/2FaPolicy.js +29 -6
  150. package/lib/mjs/policies/2FaPolicy.js.map +1 -1
  151. package/lib/mjs/services/ImpersonationService.d.ts +90 -0
  152. package/lib/mjs/services/ImpersonationService.d.ts.map +1 -0
  153. package/lib/mjs/services/ImpersonationService.js +144 -0
  154. package/lib/mjs/services/ImpersonationService.js.map +1 -0
  155. package/lib/mjs/services/SessionCookies.d.ts +41 -0
  156. package/lib/mjs/services/SessionCookies.d.ts.map +1 -0
  157. package/lib/mjs/services/SessionCookies.js +59 -0
  158. package/lib/mjs/services/SessionCookies.js.map +1 -0
  159. package/lib/mjs/services/UserMetadataService.d.ts +53 -0
  160. package/lib/mjs/services/UserMetadataService.d.ts.map +1 -0
  161. package/lib/mjs/services/UserMetadataService.js +91 -0
  162. package/lib/mjs/services/UserMetadataService.js.map +1 -0
  163. package/lib/mjs/services/grants.d.ts +29 -0
  164. package/lib/mjs/services/grants.d.ts.map +1 -0
  165. package/lib/mjs/services/grants.js +38 -0
  166. package/lib/mjs/services/grants.js.map +1 -0
  167. package/lib/tsconfig.cjs.tsbuildinfo +1 -1
  168. package/lib/tsconfig.mjs.tsbuildinfo +1 -1
  169. package/package.json +17 -11
  170. package/lib/cjs/controllers/AuthController.d.ts +0 -1
  171. package/lib/cjs/controllers/AuthController.d.ts.map +0 -1
  172. package/lib/cjs/controllers/AuthController.js +0 -211
  173. package/lib/cjs/controllers/AuthController.js.map +0 -1
  174. package/lib/cjs/controllers/FederatedLoginController.d.ts +0 -1
  175. package/lib/cjs/controllers/FederatedLoginController.d.ts.map +0 -1
  176. package/lib/cjs/controllers/FederatedLoginController.js +0 -38
  177. package/lib/cjs/controllers/FederatedLoginController.js.map +0 -1
  178. package/lib/cjs/dto/login-dto.d.ts +0 -17
  179. package/lib/cjs/dto/login-dto.d.ts.map +0 -1
  180. package/lib/cjs/dto/login-dto.js +0 -33
  181. package/lib/cjs/dto/login-dto.js.map +0 -1
  182. package/lib/cjs/dto/restore-password-dto.d.ts +0 -24
  183. package/lib/cjs/dto/restore-password-dto.d.ts.map +0 -1
  184. package/lib/cjs/dto/restore-password-dto.js.map +0 -1
  185. package/lib/cjs/dto/user-dto.d.ts +0 -22
  186. package/lib/cjs/dto/user-dto.d.ts.map +0 -1
  187. package/lib/cjs/dto/user-dto.js +0 -33
  188. package/lib/cjs/dto/user-dto.js.map +0 -1
  189. package/lib/cjs/fingerprint/FingerprintJs.d.ts +0 -1
  190. package/lib/cjs/fingerprint/FingerprintJs.d.ts.map +0 -1
  191. package/lib/cjs/fingerprint/FingerprintJs.js +0 -5
  192. package/lib/cjs/fingerprint/FingerprintJs.js.map +0 -1
  193. package/lib/cjs/middlewares/AttributeFilter.d.ts +0 -11
  194. package/lib/cjs/middlewares/AttributeFilter.d.ts.map +0 -1
  195. package/lib/cjs/middlewares/AttributeFilter.js +0 -19
  196. package/lib/cjs/middlewares/AttributeFilter.js.map +0 -1
  197. package/lib/cjs/policies/AllowFederatedLoginPolicy.d.ts +0 -7
  198. package/lib/cjs/policies/AllowFederatedLoginPolicy.d.ts.map +0 -1
  199. package/lib/cjs/policies/AllowFederatedLoginPolicy.js +0 -32
  200. package/lib/cjs/policies/AllowFederatedLoginPolicy.js.map +0 -1
  201. package/lib/cjs/policies/CaptchaPolicy.d.ts +0 -1
  202. package/lib/cjs/policies/CaptchaPolicy.d.ts.map +0 -1
  203. package/lib/cjs/policies/CaptchaPolicy.js +0 -1
  204. package/lib/cjs/policies/CaptchaPolicy.js.map +0 -1
  205. package/lib/mjs/controllers/AuthController.d.ts +0 -2
  206. package/lib/mjs/controllers/AuthController.d.ts.map +0 -1
  207. package/lib/mjs/controllers/AuthController.js +0 -212
  208. package/lib/mjs/controllers/AuthController.js.map +0 -1
  209. package/lib/mjs/controllers/FederatedLoginController.d.ts +0 -2
  210. package/lib/mjs/controllers/FederatedLoginController.d.ts.map +0 -1
  211. package/lib/mjs/controllers/FederatedLoginController.js +0 -39
  212. package/lib/mjs/controllers/FederatedLoginController.js.map +0 -1
  213. package/lib/mjs/dto/login-dto.d.ts +0 -17
  214. package/lib/mjs/dto/login-dto.d.ts.map +0 -1
  215. package/lib/mjs/dto/login-dto.js +0 -30
  216. package/lib/mjs/dto/login-dto.js.map +0 -1
  217. package/lib/mjs/dto/restore-password-dto.d.ts +0 -24
  218. package/lib/mjs/dto/restore-password-dto.d.ts.map +0 -1
  219. package/lib/mjs/dto/restore-password-dto.js +0 -31
  220. package/lib/mjs/dto/restore-password-dto.js.map +0 -1
  221. package/lib/mjs/dto/user-dto.d.ts +0 -22
  222. package/lib/mjs/dto/user-dto.d.ts.map +0 -1
  223. package/lib/mjs/dto/user-dto.js.map +0 -1
  224. package/lib/mjs/fingerprint/FingerprintJs.d.ts +0 -2
  225. package/lib/mjs/fingerprint/FingerprintJs.d.ts.map +0 -1
  226. package/lib/mjs/fingerprint/FingerprintJs.js +0 -6
  227. package/lib/mjs/fingerprint/FingerprintJs.js.map +0 -1
  228. package/lib/mjs/middlewares/AttributeFilter.d.ts +0 -11
  229. package/lib/mjs/middlewares/AttributeFilter.d.ts.map +0 -1
  230. package/lib/mjs/middlewares/AttributeFilter.js +0 -15
  231. package/lib/mjs/middlewares/AttributeFilter.js.map +0 -1
  232. package/lib/mjs/policies/AllowFederatedLoginPolicy.d.ts +0 -7
  233. package/lib/mjs/policies/AllowFederatedLoginPolicy.d.ts.map +0 -1
  234. package/lib/mjs/policies/AllowFederatedLoginPolicy.js +0 -28
  235. package/lib/mjs/policies/AllowFederatedLoginPolicy.js.map +0 -1
  236. package/lib/mjs/policies/CaptchaPolicy.d.ts +0 -2
  237. package/lib/mjs/policies/CaptchaPolicy.d.ts.map +0 -1
  238. package/lib/mjs/policies/CaptchaPolicy.js +0 -2
  239. package/lib/mjs/policies/CaptchaPolicy.js.map +0 -1
@@ -0,0 +1,54 @@
1
+ import { BaseController, BadRequestResponse, Ok } from '@spinajs/http';
2
+ import { PasswordResetConfirmDto, PasswordResetRequestDto } from '../dto/password-reset-dto.js';
3
+ /** Reply shared by both reset routes — deliberately says nothing about the account. */
4
+ export interface IPasswordResetAck {
5
+ Ok: true;
6
+ }
7
+ /**
8
+ * Password reset for users who cannot log in.
9
+ *
10
+ * Two public, unauthenticated steps: request a token, then redeem it. The token
11
+ * itself is issued into the user's metadata by `passwordChangeRequest` and
12
+ * delivered out of band (the `UserPasswordChangeRequest` event is what an app
13
+ * hooks to send the mail) — it is never returned over HTTP, so possession of
14
+ * the mailbox is what authorizes the reset.
15
+ *
16
+ * Neither route reveals whether an account exists. Both answer the same way for
17
+ * a known and an unknown address, which is the whole point of a reset endpoint
18
+ * that anybody may call: an attacker must not be able to farm valid addresses
19
+ * from it, and a failed redemption must not distinguish "no such user" from
20
+ * "wrong token".
21
+ *
22
+ * NOTE: these routes are unauthenticated and cheap to call in a loop. Put a
23
+ * rate limit in front of them at the edge — this package does not provide one.
24
+ *
25
+ * @tags Authentication
26
+ */
27
+ export declare class PasswordResetController extends BaseController {
28
+ /**
29
+ * Request a password reset
30
+ * Issues a reset token for the account and emits `UserPasswordChangeRequest` so the
31
+ * application can deliver it. Always succeeds, whether or not the address is known.
32
+ * @security []
33
+ * @returns {IPasswordResetAck} Acknowledgement — never indicates whether the account exists
34
+ */
35
+ requestReset(payload: PasswordResetRequestDto): Promise<Ok<IPasswordResetAck>>;
36
+ /**
37
+ * Complete a password reset
38
+ * Redeems the token issued by `POST /auth/password/reset-request` and sets the new
39
+ * password. The token is single-use and expires after `rbac.password.passwordResetWaitTime`.
40
+ * @security []
41
+ * @returns {IPasswordResetAck} Acknowledgement of a successful reset
42
+ * @response 400 New passwords do not match, or the token is invalid or expired
43
+ */
44
+ confirmReset(payload: PasswordResetConfirmDto): Promise<Ok<IPasswordResetAck> | BadRequestResponse>;
45
+ /**
46
+ * Issue a reset token into the user's metadata and emit
47
+ * `UserPasswordChangeRequest`. Wraps the module-level action so tests can
48
+ * stub it without a database.
49
+ */
50
+ protected issueToken(email: string): Promise<unknown>;
51
+ /** Redeem a reset token. Wrapped for the same reason as {@link issueToken}. */
52
+ protected redeemToken(email: string, password: string, token: string): Promise<unknown>;
53
+ }
54
+ //# sourceMappingURL=PasswordResetController.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PasswordResetController.d.ts","sourceRoot":"","sources":["../../../src/controllers/PasswordResetController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAkB,kBAAkB,EAAE,EAAE,EAAQ,MAAM,eAAe,CAAC;AAI7F,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAEhG,uFAAuF;AACvF,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,IAAI,CAAC;CACV;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBACa,uBAAwB,SAAQ,cAAc;IACzD;;;;;;OAMG;IAGU,YAAY,CAAS,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC;IAcnG;;;;;;;OAOG;IAGU,YAAY,CAAS,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,EAAE,CAAC,iBAAiB,CAAC,GAAG,kBAAkB,CAAC;IA+BxH;;;;OAIG;IACH,SAAS,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrD,+EAA+E;IAC/E,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAGxF"}
@@ -0,0 +1,126 @@
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 { BaseController, BasePath, Body, BadRequestResponse, Ok, Post } from '@spinajs/http';
14
+ import { confirmPasswordReset, passwordChangeRequest } from '@spinajs/rbac';
15
+ import { ErrorCode, InvalidArgument } from '@spinajs/exceptions';
16
+ import { SkipModelPermission } from '@spinajs/rbac-http';
17
+ import { PasswordResetConfirmDto, PasswordResetRequestDto } from '../dto/password-reset-dto.js';
18
+ /**
19
+ * Password reset for users who cannot log in.
20
+ *
21
+ * Two public, unauthenticated steps: request a token, then redeem it. The token
22
+ * itself is issued into the user's metadata by `passwordChangeRequest` and
23
+ * delivered out of band (the `UserPasswordChangeRequest` event is what an app
24
+ * hooks to send the mail) — it is never returned over HTTP, so possession of
25
+ * the mailbox is what authorizes the reset.
26
+ *
27
+ * Neither route reveals whether an account exists. Both answer the same way for
28
+ * a known and an unknown address, which is the whole point of a reset endpoint
29
+ * that anybody may call: an attacker must not be able to farm valid addresses
30
+ * from it, and a failed redemption must not distinguish "no such user" from
31
+ * "wrong token".
32
+ *
33
+ * NOTE: these routes are unauthenticated and cheap to call in a loop. Put a
34
+ * rate limit in front of them at the edge — this package does not provide one.
35
+ *
36
+ * @tags Authentication
37
+ */
38
+ let PasswordResetController = class PasswordResetController extends BaseController {
39
+ /**
40
+ * Request a password reset
41
+ * Issues a reset token for the account and emits `UserPasswordChangeRequest` so the
42
+ * application can deliver it. Always succeeds, whether or not the address is known.
43
+ * @security []
44
+ * @returns {IPasswordResetAck} Acknowledgement — never indicates whether the account exists
45
+ */
46
+ async requestReset(payload) {
47
+ try {
48
+ await this.issueToken(payload.Email);
49
+ }
50
+ catch (err) {
51
+ // Swallowed on purpose. An unknown address, a deleted account or a
52
+ // banned one must produce exactly the response a valid address does —
53
+ // otherwise this route becomes an account enumeration oracle. Real
54
+ // faults ( mail queue, database ) are logged for operators.
55
+ this._log.warn(err, `Password reset request could not be processed for ${payload.Email}`);
56
+ }
57
+ return new Ok({ Ok: true });
58
+ }
59
+ /**
60
+ * Complete a password reset
61
+ * Redeems the token issued by `POST /auth/password/reset-request` and sets the new
62
+ * password. The token is single-use and expires after `rbac.password.passwordResetWaitTime`.
63
+ * @security []
64
+ * @returns {IPasswordResetAck} Acknowledgement of a successful reset
65
+ * @response 400 New passwords do not match, or the token is invalid or expired
66
+ */
67
+ async confirmReset(payload) {
68
+ if (payload.Password !== payload.ConfirmPassword) {
69
+ throw new InvalidArgument('password does not match');
70
+ }
71
+ try {
72
+ await this.redeemToken(payload.Email, payload.Password, payload.Token);
73
+ }
74
+ catch (err) {
75
+ // One opaque failure for every rejection reason: unknown account, wrong
76
+ // token, expired token. Distinguishing them would tell a caller which
77
+ // addresses exist and let them probe tokens by the error they get back.
78
+ const isRedemptionFailure = err instanceof ErrorCode || err instanceof InvalidArgument;
79
+ if (!isRedemptionFailure) {
80
+ this._log.error(err, `Password reset failed unexpectedly for ${payload.Email}`);
81
+ throw err;
82
+ }
83
+ this._log.warn(err, `Password reset rejected for ${payload.Email}`);
84
+ return new BadRequestResponse({
85
+ error: {
86
+ code: 'E_RESET_TOKEN_INVALID',
87
+ message: 'Password reset token is invalid or has expired',
88
+ },
89
+ });
90
+ }
91
+ return new Ok({ Ok: true });
92
+ }
93
+ /**
94
+ * Issue a reset token into the user's metadata and emit
95
+ * `UserPasswordChangeRequest`. Wraps the module-level action so tests can
96
+ * stub it without a database.
97
+ */
98
+ issueToken(email) {
99
+ return passwordChangeRequest(email);
100
+ }
101
+ /** Redeem a reset token. Wrapped for the same reason as {@link issueToken}. */
102
+ redeemToken(email, password, token) {
103
+ return confirmPasswordReset(email, password, token);
104
+ }
105
+ };
106
+ __decorate([
107
+ Post('password/reset-request'),
108
+ SkipModelPermission(),
109
+ __param(0, Body()),
110
+ __metadata("design:type", Function),
111
+ __metadata("design:paramtypes", [PasswordResetRequestDto]),
112
+ __metadata("design:returntype", Promise)
113
+ ], PasswordResetController.prototype, "requestReset", null);
114
+ __decorate([
115
+ Post('password/reset'),
116
+ SkipModelPermission(),
117
+ __param(0, Body()),
118
+ __metadata("design:type", Function),
119
+ __metadata("design:paramtypes", [PasswordResetConfirmDto]),
120
+ __metadata("design:returntype", Promise)
121
+ ], PasswordResetController.prototype, "confirmReset", null);
122
+ PasswordResetController = __decorate([
123
+ BasePath('auth')
124
+ ], PasswordResetController);
125
+ export { PasswordResetController };
126
+ //# sourceMappingURL=PasswordResetController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PasswordResetController.js","sourceRoot":"","sources":["../../../src/controllers/PasswordResetController.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import { BaseController, NotFound, Ok } from '@spinajs/http';
2
+ import { SessionProvider, User as UserModel } from '@spinajs/rbac';
3
+ import type { ISession } from '@spinajs/rbac';
4
+ import { Log } from '@spinajs/log';
5
+ /** One live session of the calling user. */
6
+ export interface IUserSessionEntry {
7
+ /**
8
+ * Opaque, stable handle for the session. Deliberately NOT the session id:
9
+ * listing your own sessions must not hand out working credentials for them,
10
+ * because the response ( and anything that caches or logs it ) would then be
11
+ * as good as the cookies themselves.
12
+ */
13
+ Handle: string;
14
+ /** ISO instant the session was opened */
15
+ Created: string;
16
+ /** ISO instant the session expires, or null when it never does */
17
+ Expires: string | null;
18
+ /** True for the session making this request */
19
+ Current: boolean;
20
+ }
21
+ /**
22
+ * Active sessions of the calling user — "where am I logged in", plus the means
23
+ * to end any of them.
24
+ *
25
+ * OWASP asks for exactly this: the user should be able to see concurrent
26
+ * sessions and terminate them remotely, which is how a person who suspects a
27
+ * stolen cookie gets rid of it without an administrator.
28
+ *
29
+ * @tags User
30
+ */
31
+ export declare class SessionsController extends BaseController {
32
+ protected Log: Log;
33
+ protected SessionProvider: SessionProvider;
34
+ /**
35
+ * List own sessions
36
+ * Returns every live session of the authenticated user, newest first, with the
37
+ * one making the request flagged.
38
+ * @security cookieAuth
39
+ * @returns {IUserSessionEntry[]} Live sessions of the calling user
40
+ * @response 401 Unauthorized — valid session required
41
+ */
42
+ list(user: UserModel, ssid: string): Promise<Ok<IUserSessionEntry[]>>;
43
+ /**
44
+ * Revoke one session
45
+ * Ends the session identified by its handle. Revoking the current session is
46
+ * allowed and logs the caller out.
47
+ * @security cookieAuth
48
+ * @param handle Session handle as returned by `GET /user/sessions`
49
+ * @response 200 Session revoked
50
+ * @response 401 Unauthorized — valid session required
51
+ * @response 404 No such session for this user
52
+ */
53
+ revoke(user: UserModel, handle: string): Promise<Ok | NotFound>;
54
+ /**
55
+ * Revoke every other session
56
+ * Ends all sessions of the user except the one making the request — the
57
+ * "log out everywhere else" action after a suspected compromise.
58
+ * @security cookieAuth
59
+ * @response 200 Other sessions revoked
60
+ * @response 401 Unauthorized — valid session required
61
+ */
62
+ revokeOthers(user: UserModel, ssid: string): Promise<Ok>;
63
+ protected toEntry(session: ISession, currentHandle: string | null): IUserSessionEntry;
64
+ }
65
+ //# sourceMappingURL=SessionsController.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionsController.d.ts","sourceRoot":"","sources":["../../../src/controllers/SessionsController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAsB,QAAQ,EAAE,EAAE,EAAiB,MAAM,eAAe,CAAC;AAChG,OAAO,EAAE,eAAe,EAAE,IAAI,IAAI,SAAS,EAAiB,MAAM,eAAe,CAAC;AAClF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EAAE,GAAG,EAAU,MAAM,cAAc,CAAC;AAG3C,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAEhB,kEAAkE;IAClE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvB,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,qBAGa,kBAAmB,SAAQ,cAAc;IAEpD,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAGnB,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC;IAE3C;;;;;;;OAOG;IAGU,IAAI,CAAS,IAAI,EAAE,SAAS,EAAkB,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAW1G;;;;;;;;;OASG;IAGU,MAAM,CAAS,IAAI,EAAE,SAAS,EAAW,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,GAAG,QAAQ,CAAC;IAkB7F;;;;;;;OAOG;IAGU,YAAY,CAAS,IAAI,EAAE,SAAS,EAAkB,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;IAkB7F,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB;CAUtF"}
@@ -0,0 +1,140 @@
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 { BaseController, BasePath, Del, Get, NotFound, Ok, Param, Policy } from '@spinajs/http';
14
+ import { SessionProvider, User as UserModel, hashSessionId } from '@spinajs/rbac';
15
+ import { AutoinjectService } from '@spinajs/configuration';
16
+ import { Log, Logger } from '@spinajs/log';
17
+ import { AuthorizedPolicy, Permission, Resource, SessionId as SessionIdArg, User } from '@spinajs/rbac-http';
18
+ /**
19
+ * Active sessions of the calling user — "where am I logged in", plus the means
20
+ * to end any of them.
21
+ *
22
+ * OWASP asks for exactly this: the user should be able to see concurrent
23
+ * sessions and terminate them remotely, which is how a person who suspects a
24
+ * stolen cookie gets rid of it without an administrator.
25
+ *
26
+ * @tags User
27
+ */
28
+ let SessionsController = class SessionsController extends BaseController {
29
+ /**
30
+ * List own sessions
31
+ * Returns every live session of the authenticated user, newest first, with the
32
+ * one making the request flagged.
33
+ * @security cookieAuth
34
+ * @returns {IUserSessionEntry[]} Live sessions of the calling user
35
+ * @response 401 Unauthorized — valid session required
36
+ */
37
+ async list(user, ssid) {
38
+ const sessions = await this.SessionProvider.listByUser(user.Id);
39
+ const current = ssid ? hashSessionId(ssid) : null;
40
+ const entries = sessions
41
+ .map((s) => this.toEntry(s, current))
42
+ .sort((a, b) => (a.Created < b.Created ? 1 : -1));
43
+ return new Ok(entries);
44
+ }
45
+ /**
46
+ * Revoke one session
47
+ * Ends the session identified by its handle. Revoking the current session is
48
+ * allowed and logs the caller out.
49
+ * @security cookieAuth
50
+ * @param handle Session handle as returned by `GET /user/sessions`
51
+ * @response 200 Session revoked
52
+ * @response 401 Unauthorized — valid session required
53
+ * @response 404 No such session for this user
54
+ */
55
+ async revoke(user, handle) {
56
+ // Resolved through the user's OWN session list, so a handle belonging to
57
+ // somebody else's session simply is not found here — the route cannot be
58
+ // used to end a stranger's session even if its handle leaks.
59
+ const sessions = await this.SessionProvider.listByUser(user.Id);
60
+ const match = sessions.find((s) => hashSessionId(s.SessionId) === handle);
61
+ if (!match) {
62
+ return new NotFound({ error: { code: 'E_SESSION_NOT_FOUND', message: 'No such session' } });
63
+ }
64
+ await this.SessionProvider.delete(match.SessionId);
65
+ this.Log.info(`Session revoked by its owner`, { Session: handle, User: user.Uuid });
66
+ return new Ok(null, { Headers: [{ Name: 'Cache-Control', Value: 'no-store' }] });
67
+ }
68
+ /**
69
+ * Revoke every other session
70
+ * Ends all sessions of the user except the one making the request — the
71
+ * "log out everywhere else" action after a suspected compromise.
72
+ * @security cookieAuth
73
+ * @response 200 Other sessions revoked
74
+ * @response 401 Unauthorized — valid session required
75
+ */
76
+ async revokeOthers(user, ssid) {
77
+ const sessions = await this.SessionProvider.listByUser(user.Id);
78
+ let revoked = 0;
79
+ for (const session of sessions) {
80
+ if (session.SessionId === ssid) {
81
+ continue;
82
+ }
83
+ await this.SessionProvider.delete(session.SessionId);
84
+ revoked++;
85
+ }
86
+ this.Log.info(`Revoked ${revoked} other session(s)`, { User: user.Uuid });
87
+ return new Ok({ Revoked: revoked }, { Headers: [{ Name: 'Cache-Control', Value: 'no-store' }] });
88
+ }
89
+ toEntry(session, currentHandle) {
90
+ const handle = hashSessionId(session.SessionId);
91
+ return {
92
+ Handle: handle,
93
+ Created: session.Creation?.toISO() ?? '',
94
+ Expires: session.Expiration?.toISO() ?? null,
95
+ Current: handle === currentHandle,
96
+ };
97
+ }
98
+ };
99
+ __decorate([
100
+ Logger('rbac-session'),
101
+ __metadata("design:type", Log)
102
+ ], SessionsController.prototype, "Log", void 0);
103
+ __decorate([
104
+ AutoinjectService('rbac.session'),
105
+ __metadata("design:type", SessionProvider)
106
+ ], SessionsController.prototype, "SessionProvider", void 0);
107
+ __decorate([
108
+ Get('sessions'),
109
+ Permission(['readOwn']),
110
+ __param(0, User()),
111
+ __param(1, SessionIdArg()),
112
+ __metadata("design:type", Function),
113
+ __metadata("design:paramtypes", [UserModel, String]),
114
+ __metadata("design:returntype", Promise)
115
+ ], SessionsController.prototype, "list", null);
116
+ __decorate([
117
+ Del('sessions/:handle'),
118
+ Permission(['updateOwn']),
119
+ __param(0, User()),
120
+ __param(1, Param()),
121
+ __metadata("design:type", Function),
122
+ __metadata("design:paramtypes", [UserModel, String]),
123
+ __metadata("design:returntype", Promise)
124
+ ], SessionsController.prototype, "revoke", null);
125
+ __decorate([
126
+ Del('sessions'),
127
+ Permission(['updateOwn']),
128
+ __param(0, User()),
129
+ __param(1, SessionIdArg()),
130
+ __metadata("design:type", Function),
131
+ __metadata("design:paramtypes", [UserModel, String]),
132
+ __metadata("design:returntype", Promise)
133
+ ], SessionsController.prototype, "revokeOthers", null);
134
+ SessionsController = __decorate([
135
+ BasePath('user'),
136
+ Resource('user'),
137
+ Policy(AuthorizedPolicy)
138
+ ], SessionsController);
139
+ export { SessionsController };
140
+ //# sourceMappingURL=SessionsController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionsController.js","sourceRoot":"","sources":["../../../src/controllers/SessionsController.ts"],"names":[],"mappings":"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"}
@@ -1,41 +1,42 @@
1
1
  import { TokenDto } from './../dto/token-dto.js';
2
2
  import { BaseController, Ok, ForbiddenResponse } from '@spinajs/http';
3
3
  import { ISession, SessionProvider, User as UserModel, AccessControl } from '@spinajs/rbac';
4
- import { QueueService } from '@spinajs/queue';
5
- import { IEnable2faResponse, IUserWithGrants } from "@spinajs/rbac-http";
4
+ import { IEnable2faResponse, IUserWithGrants } from '@spinajs/rbac-http';
5
+ import { SessionCookieFactory } from '../services/SessionCookies.js';
6
6
  /**
7
- * Two-factor authentication (TOTP) management.
8
- * Enables, disables, and verifies TOTP-based two-factor authentication for users.
9
- * All routes are only available when 2FA is enabled in the system configuration.
10
- * The caller must be logged in but does NOT need to be fully authorized (2FA verified),
11
- * allowing these routes to be used during the 2FA verification step itself.
7
+ * Two-factor authentication during login.
8
+ *
9
+ * These routes serve the narrow window between a successful password check and
10
+ * full authorization: the session is `Logged` but not `Authorized` and carries
11
+ * the `TwoFactorAuth` marker. Both class policies must hold, so the window
12
+ * closes the moment verification succeeds.
13
+ *
14
+ * Managing a TOTP device outside that window — enrolling for the first time, or
15
+ * turning 2FA off — lives on {@link TwoFactorAuthUserController} under
16
+ * `/user/2fa`, where the caller is fully authorized. Notably there is no
17
+ * "disable" here on purpose: a session that has not yet proven possession of
18
+ * the second factor must not be able to remove it, which would reduce the whole
19
+ * scheme to the password alone.
20
+ *
12
21
  * @tags Two-Factor Authentication
13
22
  */
14
23
  export declare class TwoFactorAuthController extends BaseController {
15
- protected Queue: QueueService;
16
24
  protected SessionProvider: SessionProvider;
17
- protected SessionCookieConfig: any;
25
+ protected SessionCookies: SessionCookieFactory;
18
26
  protected AC: AccessControl;
19
27
  /**
20
- * Enable two-factor authentication
21
- * Generates a TOTP secret for the authenticated user and returns the OTP provisioning URI
22
- * to be scanned by an authenticator app. Throws if 2FA is already enabled for the user.
28
+ * Set up two-factor authentication during login
29
+ * Generates a TOTP secret for a user who must enrol before continuing — the state
30
+ * signalled by `TwoFactorInitRequired` at login when `rbac.twoFactorAuth.forceUser`
31
+ * is on. Returns the OTP provisioning URI to scan with an authenticator app; the
32
+ * caller then confirms with `POST /auth/2fa/verify`.
23
33
  * @security cookieAuth
24
34
  * @returns {IEnable2faResponse} OTP provisioning URI to scan with an authenticator app
25
35
  * @response 400 Two-factor authentication is already enabled for this user
26
36
  * @response 401 Unauthorized — valid session required
37
+ * @response 403 Session is not awaiting two-factor enrolment
27
38
  */
28
- enable2fa(user: UserModel): Promise<Ok<IEnable2faResponse>>;
29
- /**
30
- * Disable two-factor authentication
31
- * Removes the TOTP secret and disables 2FA for the authenticated user.
32
- * Throws if 2FA is not currently enabled for the user.
33
- * @security cookieAuth
34
- * @response 200 Two-factor authentication disabled successfully
35
- * @response 400 Two-factor authentication is not enabled for this user
36
- * @response 401 Unauthorized — valid session required
37
- */
38
- disable2Fa(user: UserModel): Promise<Ok<any>>;
39
+ setup2fa(user: UserModel): Promise<Ok<IEnable2faResponse>>;
39
40
  /**
40
41
  * Verify TOTP token
41
42
  * Validates the provided TOTP token against the user's 2FA secret. On success, marks the session
@@ -1 +1 @@
1
- {"version":3,"file":"TwoFactorAuthController.d.ts","sourceRoot":"","sources":["../../../src/controllers/TwoFactorAuthController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAY,EAAE,EAAa,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,IAAI,IAAI,SAAS,EAAyC,aAAa,EAA0C,MAAM,eAAe,CAAC;AAO3K,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAA6B,kBAAkB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAKpG;;;;;;;GAOG;AACH,qBAGa,uBAAwB,SAAQ,cAAc;IAEvD,SAAS,CAAC,KAAK,EAAE,YAAY,CAAC;IAG9B,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC;IAG3C,SAAS,CAAC,mBAAmB,EAAE,GAAG,CAAC;IAGnC,SAAS,CAAC,EAAE,EAAE,aAAa,CAAC;IAE5B;;;;;;;;OAQG;IAEU,SAAS,CAAS,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC;IAYhF;;;;;;;;OAQG;IAEU,UAAU,CAAS,IAAI,EAAE,SAAS;IAS/C;;;;;;;;OAQG;IAEU,WAAW,CAAS,MAAM,EAAE,SAAS,EAAU,KAAK,EAAE,QAAQ,EAAa,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,EAAE,CAAC,eAAe,CAAC,GAAG,iBAAiB,CAAC;IAgE5J;;;;OAIG;IACH,SAAS,CAAC,oBAAoB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGhF"}
1
+ {"version":3,"file":"TwoFactorAuthController.d.ts","sourceRoot":"","sources":["../../../src/controllers/TwoFactorAuthController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAY,EAAE,EAAQ,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACtF,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,IAAI,IAAI,SAAS,EAAE,aAAa,EAAqB,MAAM,eAAe,CAAC;AAO/G,OAAO,EAA6B,kBAAkB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAIpG,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAGrE;;;;;;;;;;;;;;;;GAgBG;AACH,qBAGa,uBAAwB,SAAQ,cAAc;IAEzD,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC;IAG3C,SAAS,CAAC,cAAc,EAAE,oBAAoB,CAAC;IAG/C,SAAS,CAAC,EAAE,EAAE,aAAa,CAAC;IAE5B;;;;;;;;;;;OAWG;IAEU,QAAQ,CAAS,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC;IAW/E;;;;;;;;OAQG;IAEU,WAAW,CAAS,MAAM,EAAE,SAAS,EAAU,KAAK,EAAE,QAAQ,EAAa,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,EAAE,CAAC,eAAe,CAAC,GAAG,iBAAiB,CAAC;IAyC5J;;;;OAIG;IACH,SAAS,CAAC,oBAAoB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG9E"}