@soapjs/soap-auth 0.3.3 → 0.4.4

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 (98) hide show
  1. package/.claude/settings.local.json +20 -0
  2. package/build/errors.d.ts +1 -1
  3. package/build/errors.js +2 -2
  4. package/build/index.d.ts +1 -0
  5. package/build/index.js +1 -0
  6. package/build/services/auth-throttle.service.d.ts +2 -2
  7. package/build/services/auth-throttle.service.js +2 -2
  8. package/build/services/index.d.ts +1 -0
  9. package/build/services/index.js +1 -0
  10. package/build/services/password.service.d.ts +7 -5
  11. package/build/services/password.service.js +76 -18
  12. package/build/services/totp.service.d.ts +16 -0
  13. package/build/services/totp.service.js +96 -0
  14. package/build/session/session-handler.d.ts +1 -0
  15. package/build/session/session-handler.js +39 -6
  16. package/build/soap-auth.d.ts +13 -6
  17. package/build/soap-auth.js +132 -5
  18. package/build/strategies/api-key/api-key.strategy.d.ts +4 -4
  19. package/build/strategies/api-key/api-key.strategy.js +3 -2
  20. package/build/strategies/base-auth.strategy.d.ts +5 -4
  21. package/build/strategies/basic/basic.strategy.d.ts +2 -1
  22. package/build/strategies/basic/basic.strategy.js +1 -0
  23. package/build/strategies/credential-auth.strategy.d.ts +7 -7
  24. package/build/strategies/credential-auth.strategy.js +9 -14
  25. package/build/strategies/index.d.ts +1 -0
  26. package/build/strategies/index.js +1 -0
  27. package/build/strategies/jwt/jwt.strategy.d.ts +3 -1
  28. package/build/strategies/jwt/jwt.strategy.js +41 -9
  29. package/build/strategies/jwt/jwt.tools.js +16 -14
  30. package/build/strategies/local/local.strategy.d.ts +6 -3
  31. package/build/strategies/local/local.strategy.js +83 -2
  32. package/build/strategies/oauth2/hybrid.oauth2.strategy.d.ts +3 -3
  33. package/build/strategies/oauth2/hybrid.oauth2.strategy.js +1 -6
  34. package/build/strategies/oauth2/oauth2.errors.d.ts +1 -0
  35. package/build/strategies/oauth2/oauth2.errors.js +4 -0
  36. package/build/strategies/oauth2/oauth2.strategy.d.ts +6 -4
  37. package/build/strategies/oauth2/oauth2.strategy.js +114 -46
  38. package/build/strategies/oauth2/oauth2.tools.js +2 -2
  39. package/build/strategies/oauth2/oauth2.types.d.ts +2 -2
  40. package/build/strategies/oauth2/providers/facebook.strategy.d.ts +11 -0
  41. package/build/strategies/oauth2/providers/facebook.strategy.js +58 -0
  42. package/build/strategies/oauth2/providers/github.strategy.d.ts +11 -0
  43. package/build/strategies/oauth2/providers/github.strategy.js +56 -0
  44. package/build/strategies/oauth2/providers/google.strategy.d.ts +11 -0
  45. package/build/strategies/oauth2/providers/google.strategy.js +52 -0
  46. package/build/strategies/oauth2/providers/http-oauth2.strategy.d.ts +16 -0
  47. package/build/strategies/oauth2/providers/http-oauth2.strategy.js +49 -0
  48. package/build/strategies/oauth2/providers/index.d.ts +5 -0
  49. package/build/strategies/oauth2/providers/index.js +21 -0
  50. package/build/strategies/oauth2/providers/provider.types.d.ts +7 -0
  51. package/build/strategies/oauth2/providers/provider.types.js +2 -0
  52. package/build/strategies/token-auth.strategy.d.ts +4 -4
  53. package/build/strategies/token-auth.strategy.js +2 -3
  54. package/build/tools/tools.js +1 -2
  55. package/build/types.d.ts +31 -32
  56. package/build/utils/validation.d.ts +23 -0
  57. package/build/utils/validation.js +139 -0
  58. package/package.json +8 -7
  59. package/build/__tests__/soap-auth.test.d.ts +0 -1
  60. package/build/__tests__/soap-auth.test.js +0 -42
  61. package/build/services/__tests__/account-lock.service.test.d.ts +0 -1
  62. package/build/services/__tests__/account-lock.service.test.js +0 -55
  63. package/build/services/__tests__/auth-throttle.service.test.d.ts +0 -1
  64. package/build/services/__tests__/auth-throttle.service.test.js +0 -48
  65. package/build/services/__tests__/jwks.service.test.d.ts +0 -1
  66. package/build/services/__tests__/jwks.service.test.js +0 -39
  67. package/build/services/__tests__/mfa.service.test.d.ts +0 -1
  68. package/build/services/__tests__/mfa.service.test.js +0 -66
  69. package/build/services/__tests__/password.service.test.d.ts +0 -1
  70. package/build/services/__tests__/password.service.test.js +0 -66
  71. package/build/services/__tests__/pkce.service.test.d.ts +0 -1
  72. package/build/services/__tests__/pkce.service.test.js +0 -77
  73. package/build/services/__tests__/rate-limit.service.test.d.ts +0 -1
  74. package/build/services/__tests__/rate-limit.service.test.js +0 -37
  75. package/build/services/__tests__/role.service.test.d.ts +0 -1
  76. package/build/services/__tests__/role.service.test.js +0 -31
  77. package/build/session/__tests__/file.session-store.test.d.ts +0 -1
  78. package/build/session/__tests__/file.session-store.test.js +0 -117
  79. package/build/session/__tests__/memory.session-store.test.d.ts +0 -1
  80. package/build/session/__tests__/memory.session-store.test.js +0 -77
  81. package/build/session/__tests__/session-handler.test.d.ts +0 -1
  82. package/build/session/__tests__/session-handler.test.js +0 -337
  83. package/build/strategies/__tests__/base-auth.strategy.test.d.ts +0 -14
  84. package/build/strategies/__tests__/base-auth.strategy.test.js +0 -137
  85. package/build/strategies/__tests__/credential-auth.strategy.test.d.ts +0 -14
  86. package/build/strategies/__tests__/credential-auth.strategy.test.js +0 -265
  87. package/build/strategies/__tests__/token-auth.strategy.test.d.ts +0 -28
  88. package/build/strategies/__tests__/token-auth.strategy.test.js +0 -298
  89. package/build/strategies/api-key/__tests__/api-key.strategy.test.d.ts +0 -1
  90. package/build/strategies/api-key/__tests__/api-key.strategy.test.js +0 -103
  91. package/build/strategies/basic/__tests__/basic.strategy.test.d.ts +0 -1
  92. package/build/strategies/basic/__tests__/basic.strategy.test.js +0 -104
  93. package/build/strategies/jwt/__tests__/jwt.strategy.test.d.ts +0 -1
  94. package/build/strategies/jwt/__tests__/jwt.strategy.test.js +0 -156
  95. package/build/strategies/jwt/__tests__/jwt.tools.test.d.ts +0 -1
  96. package/build/strategies/jwt/__tests__/jwt.tools.test.js +0 -98
  97. package/build/strategies/local/__tests__/local.strategy.test.d.ts +0 -1
  98. package/build/strategies/local/__tests__/local.strategy.test.js +0 -115
@@ -1,48 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const errors_1 = require("../../errors");
4
- const auth_throttle_service_1 = require("../auth-throttle.service");
5
- const mockConfig = {
6
- getFailedAttempts: jest.fn(),
7
- incrementFailedAttempts: jest.fn(),
8
- resetFailedAttempts: jest.fn(),
9
- maxFailedAttempts: 3,
10
- };
11
- const mockLogger = {
12
- warn: jest.fn(),
13
- error: jest.fn(),
14
- };
15
- describe("AuthThrottleService", () => {
16
- let service;
17
- const mockIdentifier = "user123";
18
- beforeEach(() => {
19
- jest.clearAllMocks();
20
- service = new auth_throttle_service_1.AuthThrottleService(mockConfig, mockLogger);
21
- });
22
- it("checkFailedAttempts throws error if max failed attempts reached", async () => {
23
- mockConfig.getFailedAttempts.mockResolvedValue(3);
24
- await expect(service.checkFailedAttempts(mockIdentifier)).rejects.toThrow(errors_1.AccountLockedError);
25
- expect(mockLogger.warn).toHaveBeenCalledWith(`User ${mockIdentifier} is temporarily locked out.`);
26
- });
27
- it("checkFailedAttempts does not throw error if failed attempts are below threshold", async () => {
28
- mockConfig.getFailedAttempts.mockResolvedValue(2);
29
- await expect(service.checkFailedAttempts(mockIdentifier)).resolves.not.toThrow();
30
- });
31
- it("checkFailedAttempts logs error if an exception occurs", async () => {
32
- mockConfig.getFailedAttempts.mockRejectedValue(new Error("DB error"));
33
- await service.checkFailedAttempts(mockIdentifier);
34
- expect(mockLogger.error).toHaveBeenCalledWith("Check failed attempts:", expect.any(Error));
35
- });
36
- it("incrementFailedAttempts increments counter and throws if max reached", async () => {
37
- mockConfig.getFailedAttempts.mockResolvedValue(3);
38
- await expect(service.incrementFailedAttempts(mockIdentifier)).rejects.toThrow(errors_1.AccountLockedError);
39
- });
40
- it("incrementFailedAttempts increments counter without throwing if below threshold", async () => {
41
- mockConfig.getFailedAttempts.mockResolvedValue(2);
42
- await expect(service.incrementFailedAttempts(mockIdentifier)).resolves.not.toThrow();
43
- });
44
- it("resetFailedAttempts calls the correct method", async () => {
45
- await service.resetFailedAttempts(mockIdentifier);
46
- expect(mockConfig.resetFailedAttempts).toHaveBeenCalledWith(mockIdentifier);
47
- });
48
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,39 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
7
- const jwks_service_1 = require("../jwks.service");
8
- const oauth2_errors_1 = require("../../strategies/oauth2/oauth2.errors");
9
- describe("JwtService", () => {
10
- let service;
11
- let mockConfig;
12
- const mockIdToken = "mock.id.token";
13
- beforeEach(() => {
14
- jest.clearAllMocks();
15
- mockConfig = {
16
- jwks: {
17
- jwksUri: "https://mock-jwks-uri.com",
18
- algorithms: ["RS256"],
19
- issuer: "mock-issuer",
20
- audience: "mock-client-id",
21
- },
22
- };
23
- service = new jwks_service_1.JwtService(mockConfig);
24
- });
25
- it("verify throws error if ID token structure is invalid", async () => {
26
- jest.spyOn(jsonwebtoken_1.default, "decode").mockReturnValue(null);
27
- await expect(service.verify(mockIdToken)).rejects.toThrow("Invalid ID Token structure.");
28
- });
29
- it("verify throws error if ID token is expired", async () => {
30
- jest.spyOn(jsonwebtoken_1.default, "decode").mockReturnValue({ header: { kid: "mock-kid" } });
31
- jest
32
- .spyOn(service.client, "getSigningKey")
33
- .mockResolvedValue({ getPublicKey: () => "mock-public-key" });
34
- jest
35
- .spyOn(jsonwebtoken_1.default, "verify")
36
- .mockReturnValue({ exp: Math.floor(Date.now() / 1000) - 10 });
37
- await expect(service.verify(mockIdToken)).rejects.toThrow(oauth2_errors_1.InvalidIdTokenError);
38
- });
39
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,66 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const mfa_service_1 = require("../mfa.service");
4
- const mockConfig = {
5
- isMfaRequired: jest.fn(),
6
- extractMfaCode: jest.fn(),
7
- sendMfaCode: jest.fn(),
8
- validateMfaCode: jest.fn(),
9
- maxMfaAttempts: 3,
10
- getMfaAttempts: jest.fn(),
11
- incrementMfaAttempts: jest.fn(),
12
- resetMfaAttempts: jest.fn(),
13
- lockMfaOnFailure: jest.fn(),
14
- };
15
- const mockLogger = {
16
- info: jest.fn(),
17
- warn: jest.fn(),
18
- error: jest.fn(),
19
- };
20
- describe("MfaService", () => {
21
- let service;
22
- const mockUser = { id: "user123" };
23
- const mockContext = { token: "mock-token" };
24
- beforeEach(() => {
25
- jest.clearAllMocks();
26
- service = new mfa_service_1.MfaService(mockConfig, mockLogger);
27
- });
28
- it("checkMfa sends code if required and no code provided", async () => {
29
- mockConfig.isMfaRequired.mockReturnValue(true);
30
- mockConfig.extractMfaCode.mockReturnValue(null);
31
- await expect(service.checkMfa(mockUser, mockContext)).rejects.toThrow("2FA required. A verification code has been sent.");
32
- expect(mockConfig.sendMfaCode).toHaveBeenCalledWith(mockUser, mockContext);
33
- });
34
- it("checkMfa locks account after too many failed attempts", async () => {
35
- mockConfig.isMfaRequired.mockReturnValue(true);
36
- mockConfig.extractMfaCode.mockReturnValue("wrong-code");
37
- mockConfig.getMfaAttempts.mockResolvedValue(3);
38
- await expect(service.checkMfa(mockUser, mockContext)).rejects.toThrow("Your account has been temporarily locked due to too many failed 2FA attempts.");
39
- expect(mockLogger.warn).toHaveBeenCalledWith(`User ${mockUser} exceeded maximum MFA attempts.`);
40
- expect(mockConfig.lockMfaOnFailure).toHaveBeenCalledWith(mockUser);
41
- });
42
- it("checkMfa rejects invalid MFA codes", async () => {
43
- mockConfig.isMfaRequired.mockReturnValue(true);
44
- mockConfig.extractMfaCode.mockReturnValue("invalid-code");
45
- mockConfig.getMfaAttempts.mockResolvedValue(1);
46
- mockConfig.validateMfaCode.mockResolvedValue(false);
47
- await expect(service.checkMfa(mockUser, mockContext)).rejects.toThrow("Invalid 2FA code provided.");
48
- expect(mockLogger.warn).toHaveBeenCalledWith(`Invalid MFA code attempt for user: ${mockUser}`);
49
- expect(mockConfig.incrementMfaAttempts).toHaveBeenCalledWith(mockUser);
50
- });
51
- it("checkMfa resets attempts on successful MFA validation", async () => {
52
- mockConfig.isMfaRequired.mockReturnValue(true);
53
- mockConfig.extractMfaCode.mockReturnValue("valid-code");
54
- mockConfig.validateMfaCode.mockResolvedValue(true);
55
- await expect(service.checkMfa(mockUser, mockContext)).resolves.not.toThrow();
56
- expect(mockConfig.resetMfaAttempts).toHaveBeenCalledWith(mockUser);
57
- expect(mockLogger.info).toHaveBeenCalledWith(`2FA successfully validated for user: ${mockUser}`);
58
- });
59
- it("lockMfaOnFailure logs error if an exception occurs", async () => {
60
- mockConfig.lockMfaOnFailure.mockImplementation(() => {
61
- throw new Error("Lock failed");
62
- });
63
- service.lockMfaOnFailure(mockUser);
64
- expect(mockLogger.error).toHaveBeenCalledWith(expect.any(Error));
65
- });
66
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,66 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const password_service_1 = require("../password.service");
4
- const soap_1 = require("@soapjs/soap");
5
- const globals_1 = require("@jest/globals");
6
- describe("PasswordService", () => {
7
- let service;
8
- let mockConfig;
9
- const mockLogger = { error: globals_1.jest.fn() };
10
- const mockIdentifier = "user123";
11
- const mockPassword = "SecurePass123!";
12
- beforeEach(() => {
13
- globals_1.jest.clearAllMocks();
14
- mockConfig = {
15
- validatePassword: globals_1.jest.fn(),
16
- getLastPasswordChange: globals_1.jest.fn(),
17
- generateResetToken: globals_1.jest.fn(),
18
- sendResetEmail: globals_1.jest.fn(),
19
- validateResetToken: globals_1.jest.fn(),
20
- updatePassword: globals_1.jest.fn(),
21
- passwordExpirationDays: 30,
22
- };
23
- service = new password_service_1.PasswordService(mockConfig, mockLogger);
24
- });
25
- it("validatePassword calls config method", async () => {
26
- mockConfig.validatePassword.mockReturnValue(true);
27
- await expect(service.validatePassword(mockPassword)).resolves.toBe(true);
28
- expect(mockConfig.validatePassword).toHaveBeenCalledWith(mockPassword);
29
- });
30
- it("getLastPasswordChange calls config method", async () => {
31
- const mockDate = new Date();
32
- mockConfig.getLastPasswordChange.mockReturnValue(mockDate);
33
- await expect(service.getLastPasswordChange(mockIdentifier)).resolves.toBe(mockDate);
34
- expect(mockConfig.getLastPasswordChange).toHaveBeenCalledWith(mockIdentifier);
35
- });
36
- it("generateResetToken calls config method", async () => {
37
- mockConfig.generateResetToken.mockResolvedValue("mock-token");
38
- await expect(service.generateResetToken(mockIdentifier)).resolves.toBe("mock-token");
39
- });
40
- it("sendResetEmail calls config method", async () => {
41
- await expect(service.sendResetEmail(mockIdentifier, "mock-token")).resolves.not.toThrow();
42
- expect(mockConfig.sendResetEmail).toHaveBeenCalledWith(mockIdentifier, "mock-token");
43
- });
44
- it("validateResetToken calls config method", async () => {
45
- mockConfig.validateResetToken.mockResolvedValue(true);
46
- await expect(service.validateResetToken("mock-token")).resolves.toBe(true);
47
- });
48
- it("updatePassword calls config method", async () => {
49
- await expect(service.updatePassword(mockIdentifier, mockPassword)).resolves.not.toThrow();
50
- expect(mockConfig.updatePassword).toHaveBeenCalledWith(mockIdentifier, mockPassword);
51
- });
52
- it("isPasswordChangeRequired returns true if password is expired", async () => {
53
- const pastDate = new Date(Date.now() - 31 * 86400000);
54
- mockConfig.getLastPasswordChange.mockResolvedValue(pastDate);
55
- await expect(service.isPasswordChangeRequired(mockIdentifier)).resolves.toBe(true);
56
- });
57
- it("isPasswordChangeRequired returns false if password is within expiration period", async () => {
58
- const recentDate = new Date(Date.now() - 15 * 86400000);
59
- mockConfig.getLastPasswordChange.mockResolvedValue(recentDate);
60
- await expect(service.isPasswordChangeRequired(mockIdentifier)).resolves.toBe(false);
61
- });
62
- it("isPasswordChangeRequired throws NotImplementedError if getLastPasswordChange is not defined", async () => {
63
- service = new password_service_1.PasswordService({ passwordExpirationDays: 30 }, mockLogger);
64
- await expect(service.isPasswordChangeRequired(mockIdentifier)).rejects.toThrow(soap_1.NotImplementedError);
65
- });
66
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,77 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const pkce_service_1 = require("../pkce.service");
4
- class InMemoryPKCEPersistence {
5
- storeMap = new Map();
6
- async store(key, meta) {
7
- this.storeMap.set(key, meta || {});
8
- }
9
- async read(verifierOrChallenge) {
10
- return this.storeMap.get(verifierOrChallenge);
11
- }
12
- async remove(key) {
13
- this.storeMap.delete(key);
14
- }
15
- }
16
- describe("PKCEService", () => {
17
- let service;
18
- let config;
19
- beforeEach(() => {
20
- config = {
21
- verifier: {
22
- expiresIn: 1,
23
- embed: jest.fn(),
24
- extract: jest.fn().mockImplementation((ctx) => ctx.verifier),
25
- persistence: new InMemoryPKCEPersistence(),
26
- },
27
- challenge: {
28
- expiresIn: 1,
29
- embed: jest.fn(),
30
- extract: jest.fn().mockImplementation((ctx) => ctx.challenge),
31
- persistence: new InMemoryPKCEPersistence(),
32
- },
33
- };
34
- service = new pkce_service_1.PKCEService(config);
35
- });
36
- it("should generate and store a code verifier", async () => {
37
- const context = { key: "code_verifier_test" };
38
- const verifier = await service.generateCodeVerifier(context);
39
- expect(verifier).toBeDefined();
40
- expect(config.verifier.embed).toHaveBeenCalledWith(context, verifier);
41
- });
42
- it("should generate and store a code challenge", async () => {
43
- const context = { key: "code_challenge_test" };
44
- const challenge = await service.generateCodeChallenge("test_verifier", context);
45
- expect(challenge).toBeDefined();
46
- expect(config.challenge.embed).toHaveBeenCalledWith(context, challenge);
47
- });
48
- it("should detect expired code verifier", async () => {
49
- config.verifier.embed = (ctx, cv) => {
50
- ctx.verifier = cv;
51
- };
52
- const context = { key: "expired_verifier_test" };
53
- await service.generateCodeVerifier(context);
54
- await new Promise((resolve) => setTimeout(resolve, 1500));
55
- const isExpired = await service.isCodeVerifierExpired(context);
56
- expect(isExpired).toBe(true);
57
- });
58
- it("should detect not-expired code verifier", async () => {
59
- config.verifier.embed = (ctx, cv) => {
60
- ctx.verifier = cv;
61
- };
62
- const context = {
63
- key: "not_expired_verifier_test",
64
- };
65
- await service.generateCodeVerifier(context);
66
- await new Promise((resolve) => setTimeout(resolve, 500));
67
- const isExpired = await service.isCodeVerifierExpired(context);
68
- expect(isExpired).toBe(false);
69
- });
70
- it("should clear code verifier", async () => {
71
- const context = {
72
- key: "clear_verifier_test",
73
- verifier: "test_verifier_value",
74
- };
75
- await service.clearCodeVerifier(context);
76
- });
77
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,37 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const errors_1 = require("../../errors");
4
- const rate_limit_service_1 = require("../rate-limit.service");
5
- const mockConfig = {
6
- incrementRequestCount: jest.fn(),
7
- checkRateLimit: jest.fn(),
8
- };
9
- const mockLogger = {
10
- error: jest.fn(),
11
- };
12
- describe("RateLimitService", () => {
13
- let service;
14
- const mockData = { userId: "user123" };
15
- beforeEach(() => {
16
- jest.clearAllMocks();
17
- service = new rate_limit_service_1.RateLimitService(mockConfig, mockLogger);
18
- });
19
- it("incrementRequestCount calls config method and handles errors", async () => {
20
- mockConfig.incrementRequestCount.mockResolvedValue(undefined);
21
- await expect(service.incrementRequestCount(mockData)).resolves.not.toThrow();
22
- expect(mockConfig.incrementRequestCount).toHaveBeenCalledWith(mockData);
23
- });
24
- it("incrementRequestCount logs error if an exception occurs", async () => {
25
- mockConfig.incrementRequestCount.mockRejectedValue(new Error("Increment failed"));
26
- await expect(service.incrementRequestCount(mockData)).resolves.not.toThrow();
27
- expect(mockLogger.error).toHaveBeenCalledWith(expect.any(Error));
28
- });
29
- it("checkRateLimit throws RateLimitExceededError if limit is exceeded", async () => {
30
- mockConfig.checkRateLimit.mockResolvedValue(true);
31
- await expect(service.checkRateLimit(mockData)).rejects.toThrow(errors_1.RateLimitExceededError);
32
- });
33
- it("checkRateLimit does not throw error if limit is not exceeded", async () => {
34
- mockConfig.checkRateLimit.mockResolvedValue(false);
35
- await expect(service.checkRateLimit(mockData)).resolves.not.toThrow();
36
- });
37
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,31 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const errors_1 = require("../../errors");
4
- const role_service_1 = require("../role.service");
5
- const mockConfig = {
6
- authorizeByRoles: jest.fn(),
7
- roles: ["admin", "editor"],
8
- };
9
- const mockLogger = {
10
- error: jest.fn(),
11
- };
12
- describe("RoleService", () => {
13
- let service;
14
- const mockUser = { id: "user123", role: "viewer" };
15
- beforeEach(() => {
16
- jest.clearAllMocks();
17
- service = new role_service_1.RoleService(mockConfig, mockLogger);
18
- });
19
- it("isAuthorized throws UnauthorizedRoleError if user is not authorized", async () => {
20
- mockConfig.authorizeByRoles.mockResolvedValue(false);
21
- await expect(service.isAuthorized(mockUser)).rejects.toThrow(errors_1.UnauthorizedRoleError);
22
- });
23
- it("isAuthorized returns true if user is authorized", async () => {
24
- mockConfig.authorizeByRoles.mockResolvedValue(true);
25
- await expect(service.isAuthorized(mockUser)).resolves.toBe(true);
26
- });
27
- it("isAuthorized bypasses role check if authorizeByRoles is not defined", async () => {
28
- service = new role_service_1.RoleService({ roles: [] }, mockLogger);
29
- await expect(service.isAuthorized(mockUser)).resolves.toBe(true);
30
- });
31
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,117 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const file_session_store_1 = require("../file.session-store");
7
- const promises_1 = __importDefault(require("fs/promises"));
8
- const path_1 = __importDefault(require("path"));
9
- jest.mock("fs/promises", () => ({
10
- mkdir: jest.fn(),
11
- readFile: jest.fn(),
12
- writeFile: jest.fn(),
13
- unlink: jest.fn(),
14
- readdir: jest.fn(),
15
- }));
16
- describe("FileSessionStore", () => {
17
- let store;
18
- const mockSessionsDir = "/mock/sessions";
19
- beforeEach(() => {
20
- store = new file_session_store_1.FileSessionStore(mockSessionsDir);
21
- jest.clearAllMocks();
22
- });
23
- describe("init", () => {
24
- it("should create the sessions directory recursively", async () => {
25
- await store.init();
26
- expect(promises_1.default.mkdir).toHaveBeenCalledWith(mockSessionsDir, {
27
- recursive: true,
28
- });
29
- });
30
- it("should catch and log errors if mkdir fails", async () => {
31
- const consoleSpy = jest
32
- .spyOn(console, "error")
33
- .mockImplementation(() => { });
34
- promises_1.default.mkdir.mockRejectedValueOnce(new Error("mkdir error"));
35
- await store.init();
36
- expect(consoleSpy).toHaveBeenCalledWith("Error initializing session directory:", expect.any(Error));
37
- consoleSpy.mockRestore();
38
- });
39
- });
40
- describe("getSession", () => {
41
- it("should return parsed JSON if file read is successful", async () => {
42
- const mockData = { user: { id: "123" } };
43
- promises_1.default.readFile.mockResolvedValueOnce(JSON.stringify(mockData));
44
- const session = await store.getSession("session123");
45
- expect(session).toEqual(mockData);
46
- expect(promises_1.default.readFile).toHaveBeenCalledWith(path_1.default.join(mockSessionsDir, "session123"), "utf8");
47
- });
48
- it("should return null and log error if readFile fails", async () => {
49
- const consoleSpy = jest
50
- .spyOn(console, "error")
51
- .mockImplementation(() => { });
52
- promises_1.default.readFile.mockRejectedValueOnce(new Error("read error"));
53
- const session = await store.getSession("badSession");
54
- expect(session).toBeNull();
55
- expect(consoleSpy).toHaveBeenCalledWith("Error getting session:", expect.any(Error));
56
- consoleSpy.mockRestore();
57
- });
58
- it("should return null if JSON parse fails", async () => {
59
- const consoleSpy = jest
60
- .spyOn(console, "error")
61
- .mockImplementation(() => { });
62
- promises_1.default.readFile.mockResolvedValueOnce("invalid-json");
63
- const session = await store.getSession("corruptSession");
64
- expect(session).toBeNull();
65
- expect(consoleSpy).toHaveBeenCalledWith("Error getting session:", expect.any(Error));
66
- consoleSpy.mockRestore();
67
- });
68
- });
69
- describe("setSession", () => {
70
- it("should write stringified session data to a file", async () => {
71
- const mockData = { user: { id: "xyz" } };
72
- await store.setSession("sessionABC", mockData);
73
- expect(promises_1.default.writeFile).toHaveBeenCalledWith(path_1.default.join(mockSessionsDir, "sessionABC"), JSON.stringify(mockData), "utf8");
74
- });
75
- it("should throw error if writeFile fails", async () => {
76
- promises_1.default.writeFile.mockRejectedValueOnce(new Error("write error"));
77
- await expect(store.setSession("sessionError", { user: { id: "1" } })).rejects.toThrow("write error");
78
- });
79
- });
80
- describe("destroySession", () => {
81
- it("should unlink the file associated with the session ID", async () => {
82
- await store.destroySession("destroyMe");
83
- expect(promises_1.default.unlink).toHaveBeenCalledWith(path_1.default.join(mockSessionsDir, "destroyMe"));
84
- });
85
- it("should ignore ENOENT errors (file not found)", async () => {
86
- promises_1.default.unlink.mockRejectedValueOnce({ code: "ENOENT" });
87
- await expect(store.destroySession("notExists")).resolves.toBeUndefined();
88
- });
89
- it("should rethrow other errors", async () => {
90
- promises_1.default.unlink.mockRejectedValueOnce(new Error("unlink error"));
91
- await expect(store.destroySession("sessionX")).rejects.toThrow("unlink error");
92
- });
93
- });
94
- describe("touchSession", () => {
95
- it("should call setSession with updated data", async () => {
96
- const setSessionSpy = jest.spyOn(store, "setSession").mockResolvedValue();
97
- const newData = { user: { id: "touchMe" } };
98
- await store.touchSession("touchId", newData);
99
- expect(setSessionSpy).toHaveBeenCalledWith("touchId", newData);
100
- });
101
- });
102
- describe("getSessionIds", () => {
103
- it("should return the base names of files in the sessions directory", async () => {
104
- promises_1.default.readdir.mockResolvedValueOnce([
105
- { name: "abc", isFile: () => true },
106
- { name: "def.json", isFile: () => true },
107
- { name: "subdir", isFile: () => false },
108
- ]);
109
- const result = await store.getSessionIds();
110
- expect(result).toEqual(["abc", "def"]);
111
- });
112
- it("should throw if readdir fails", async () => {
113
- promises_1.default.readdir.mockRejectedValueOnce(new Error("readdir error"));
114
- await expect(store.getSessionIds()).rejects.toThrow("readdir error");
115
- });
116
- });
117
- });
@@ -1,77 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const memory_session_store_1 = require("../memory.session-store");
4
- describe("MemorySessionStore", () => {
5
- let store;
6
- beforeEach(() => {
7
- store = new memory_session_store_1.MemorySessionStore();
8
- });
9
- describe("getSession", () => {
10
- it("should return null if session does not exist", async () => {
11
- const data = await store.getSession("nonExistent");
12
- expect(data).toBeNull();
13
- });
14
- it("should return the stored session data if present", async () => {
15
- await store.setSession("session1", {
16
- user: { id: "123", name: "John" },
17
- });
18
- const session = await store.getSession("session1");
19
- expect(session).toEqual({ user: { id: "123", name: "John" } });
20
- });
21
- });
22
- describe("setSession", () => {
23
- it("should store session data in memory", async () => {
24
- await store.setSession("session2", {
25
- user: { id: "abc", name: "Alice" },
26
- });
27
- const session = await store.getSession("session2");
28
- expect(session).toEqual({ user: { id: "abc", name: "Alice" } });
29
- });
30
- });
31
- describe("destroySession", () => {
32
- it("should delete the session from memory", async () => {
33
- await store.setSession("sessionDelete", {
34
- user: { id: "xyz", name: "Jane" },
35
- });
36
- await store.destroySession("sessionDelete");
37
- const session = await store.getSession("sessionDelete");
38
- expect(session).toBeNull();
39
- });
40
- it("should not throw if the session does not exist", async () => {
41
- await expect(store.destroySession("noSession")).resolves.not.toThrow();
42
- });
43
- });
44
- describe("touchSession", () => {
45
- it("should update the session data if it exists", async () => {
46
- await store.setSession("sessionTouch", {
47
- user: { id: "initial", name: "Old" },
48
- });
49
- await store.touchSession("sessionTouch", {
50
- user: { id: "updated", name: "New" },
51
- });
52
- const updated = await store.getSession("sessionTouch");
53
- expect(updated).toEqual({ user: { id: "updated", name: "New" } });
54
- });
55
- it("should create a new session if it doesn't exist (similar to setSession)", async () => {
56
- await store.touchSession("sessionNew", {
57
- user: { id: "newId", name: "New Name" },
58
- });
59
- const data = await store.getSession("sessionNew");
60
- expect(data).toEqual({ user: { id: "newId", name: "New Name" } });
61
- });
62
- });
63
- describe("getSessionIds", () => {
64
- it("should return an empty array if no sessions exist", async () => {
65
- const ids = await store.getSessionIds();
66
- expect(ids).toEqual([]);
67
- });
68
- it("should return all session IDs currently in memory", async () => {
69
- await store.setSession("id1", { user: { id: "one" } });
70
- await store.setSession("id2", { user: { id: "two" } });
71
- const ids = await store.getSessionIds();
72
- expect(ids).toContain("id1");
73
- expect(ids).toContain("id2");
74
- expect(ids.length).toBe(2);
75
- });
76
- });
77
- });
@@ -1 +0,0 @@
1
- export {};