@oneuptime/common 12.0.21 → 12.0.22

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 (40) hide show
  1. package/Models/DatabaseModels/Index.ts +2 -0
  2. package/Models/DatabaseModels/UserTwoFactorBackupCode.ts +262 -0
  3. package/Server/API/UserTwoFactorBackupCodeAPI.ts +258 -0
  4. package/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddUserTwoFactorBackupCode.ts +63 -0
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  6. package/Server/Services/Index.ts +2 -0
  7. package/Server/Services/UserService.ts +42 -0
  8. package/Server/Services/UserTwoFactorBackupCodeService.ts +355 -0
  9. package/Server/Utils/TwoFactorBackupCode.ts +266 -0
  10. package/Tests/Server/API/UserAuthenticationAPI.test.ts +16 -3
  11. package/Tests/Server/API/UserTwoFactorAuthAdminAPI.test.ts +18 -2
  12. package/Tests/Server/API/UserTwoFactorBackupCodeAPI.test.ts +1390 -0
  13. package/Tests/Server/Services/UserAuthenticationService.test.ts +23 -1
  14. package/Tests/Server/Services/UserTwoFactorAuthAdmin.test.ts +21 -0
  15. package/Tests/Server/Services/UserTwoFactorBackupCodeAdminSurface.test.ts +919 -0
  16. package/Tests/Server/Services/UserTwoFactorBackupCodeService.test.ts +862 -0
  17. package/Tests/Server/Utils/TwoFactorBackupCode.test.ts +475 -0
  18. package/Types/Email/EmailTemplateType.ts +2 -0
  19. package/Types/UserAuthenticationStatus.ts +16 -0
  20. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  21. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  22. package/build/dist/Models/DatabaseModels/UserTwoFactorBackupCode.js +277 -0
  23. package/build/dist/Models/DatabaseModels/UserTwoFactorBackupCode.js.map +1 -0
  24. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js +201 -0
  25. package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js.map +1 -0
  26. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddUserTwoFactorBackupCode.js +46 -0
  27. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddUserTwoFactorBackupCode.js.map +1 -0
  28. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  29. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  30. package/build/dist/Server/Services/Index.js +2 -0
  31. package/build/dist/Server/Services/Index.js.map +1 -1
  32. package/build/dist/Server/Services/UserService.js +39 -0
  33. package/build/dist/Server/Services/UserService.js.map +1 -1
  34. package/build/dist/Server/Services/UserTwoFactorBackupCodeService.js +327 -0
  35. package/build/dist/Server/Services/UserTwoFactorBackupCodeService.js.map +1 -0
  36. package/build/dist/Server/Utils/TwoFactorBackupCode.js +269 -0
  37. package/build/dist/Server/Utils/TwoFactorBackupCode.js.map +1 -0
  38. package/build/dist/Types/Email/EmailTemplateType.js +2 -0
  39. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  40. package/package.json +1 -1
@@ -96,6 +96,14 @@ const AUTHENTICATION_STATUS: UserAuthenticationStatus = {
96
96
  */
97
97
  twoFactorAuthStatus: TwoFactorAuthStatus.EnabledPendingSetup,
98
98
  verifiedTwoFactorAuthMethodCount: 0,
99
+
100
+ /*
101
+ * Reported alongside the method count, never folded into it -- backup codes
102
+ * are the way back in to an account whose factor is unreachable, not a
103
+ * factor. Zero here keeps this fixture the pure "mandated, nothing set up"
104
+ * case it exists to be.
105
+ */
106
+ unusedTwoFactorBackupCodeCount: 0,
99
107
  hasPendingPasswordResetLink: false,
100
108
  };
101
109
 
@@ -201,9 +209,13 @@ describe("GET /user/:userId/authentication-status", () => {
201
209
 
202
210
  test("sends exactly what the service reported", async () => {
203
211
  /*
204
- * The page renders these four booleans directly. A field dropped or
205
- * renamed on the way out reads as `undefined`, which the UI shows as "no
206
- * password" for an account that has one.
212
+ * The page renders these fields directly. One dropped or renamed on the
213
+ * way out reads as `undefined`, which the UI shows as "no password" for an
214
+ * account that has one.
215
+ *
216
+ * Asserted as an EXACT object rather than field by field, so a new field
217
+ * added to the DTO has to be added here too -- which is how this test has
218
+ * done its job twice now, most recently for the backup code count.
207
219
  */
208
220
  await callRoute({
209
221
  method: "GET",
@@ -217,6 +229,7 @@ describe("GET /user/:userId/authentication-status", () => {
217
229
  isTwoFactorAuthEnabled: true,
218
230
  twoFactorAuthStatus: TwoFactorAuthStatus.EnabledPendingSetup,
219
231
  verifiedTwoFactorAuthMethodCount: 0,
232
+ unusedTwoFactorBackupCodeCount: 0,
220
233
  hasPendingPasswordResetLink: false,
221
234
  });
222
235
  });
@@ -792,6 +792,7 @@ describe("GET /user/:userId/authentication-status two factor auth fields", () =>
792
792
  isTwoFactorAuthEnabled: true,
793
793
  twoFactorAuthStatus: TwoFactorAuthStatus.EnabledPendingSetup,
794
794
  verifiedTwoFactorAuthMethodCount: 0,
795
+ unusedTwoFactorBackupCodeCount: 0,
795
796
  hasPendingPasswordResetLink: false,
796
797
  };
797
798
 
@@ -826,6 +827,7 @@ describe("GET /user/:userId/authentication-status two factor auth fields", () =>
826
827
  isTwoFactorAuthEnabled: true,
827
828
  twoFactorAuthStatus: TwoFactorAuthStatus.EnabledPendingSetup,
828
829
  verifiedTwoFactorAuthMethodCount: 0,
830
+ unusedTwoFactorBackupCodeCount: 0,
829
831
  hasPendingPasswordResetLink: false,
830
832
  });
831
833
  });
@@ -863,6 +865,9 @@ describe("GET /user/:userId/authentication-status two factor auth fields", () =>
863
865
 
864
866
  expect(Object.keys(payload)).toContain("verifiedTwoFactorAuthMethodCount");
865
867
  expect(payload["verifiedTwoFactorAuthMethodCount"]).toBe(0);
868
+
869
+ expect(Object.keys(payload)).toContain("unusedTwoFactorBackupCodeCount");
870
+ expect(payload["unusedTwoFactorBackupCodeCount"]).toBe(0);
866
871
  });
867
872
 
868
873
  test("reports a configured account with its real method count", async () => {
@@ -871,14 +876,17 @@ describe("GET /user/:userId/authentication-status two factor auth fields", () =>
871
876
  * operator would reset a user who was perfectly fine and lock them out
872
877
  * themselves.
873
878
  */
874
- statusSpy.mockResolvedValue({
879
+ const configuredStatus: UserAuthenticationStatus = {
875
880
  hasPassword: false,
876
881
  isEmailVerified: true,
877
882
  isTwoFactorAuthEnabled: true,
878
883
  twoFactorAuthStatus: TwoFactorAuthStatus.EnabledConfigured,
879
884
  verifiedTwoFactorAuthMethodCount: 2,
885
+ unusedTwoFactorBackupCodeCount: 5,
880
886
  hasPendingPasswordResetLink: false,
881
- } as UserAuthenticationStatus);
887
+ };
888
+
889
+ statusSpy.mockResolvedValue(configuredStatus);
882
890
 
883
891
  await callRoute({
884
892
  method: "GET",
@@ -892,6 +900,14 @@ describe("GET /user/:userId/authentication-status two factor auth fields", () =>
892
900
  TwoFactorAuthStatus.EnabledConfigured,
893
901
  );
894
902
  expect(payload["verifiedTwoFactorAuthMethodCount"]).toBe(2);
903
+
904
+ /*
905
+ * Carried alongside the method count, never folded into it. This is the
906
+ * number that tells an operator whether the user can get back in without
907
+ * them -- so a reset that would sign the user out everywhere is only the
908
+ * right button when it is zero.
909
+ */
910
+ expect(payload["unusedTwoFactorBackupCodeCount"]).toBe(5);
895
911
  });
896
912
 
897
913
  test("sets the no-cache headers before the payload goes out", async () => {