@sync-in/server 1.3.9 → 1.4.0

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 (99) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +3 -2
  3. package/environment/environment.dist.yaml +2 -0
  4. package/package.json +2 -2
  5. package/server/app.bootstrap.js +9 -0
  6. package/server/app.bootstrap.js.map +1 -1
  7. package/server/app.service.spec.js +44 -19
  8. package/server/app.service.spec.js.map +1 -1
  9. package/server/applications/files/constants/files.js +0 -23
  10. package/server/applications/files/constants/files.js.map +1 -1
  11. package/server/applications/files/constants/only-office.js +8 -0
  12. package/server/applications/files/constants/only-office.js.map +1 -1
  13. package/server/applications/files/files.config.js +5 -0
  14. package/server/applications/files/files.config.js.map +1 -1
  15. package/server/applications/files/guards/files-only-office.strategy.js +0 -1
  16. package/server/applications/files/guards/files-only-office.strategy.js.map +1 -1
  17. package/server/applications/spaces/guards/space.guard.spec.js +153 -18
  18. package/server/applications/spaces/guards/space.guard.spec.js.map +1 -1
  19. package/server/applications/spaces/services/spaces-browser.service.js +7 -7
  20. package/server/applications/spaces/services/spaces-browser.service.js.map +1 -1
  21. package/server/applications/spaces/services/spaces-manager.service.js +17 -17
  22. package/server/applications/spaces/services/spaces-manager.service.js.map +1 -1
  23. package/server/applications/sync/utils/routes.js +1 -1
  24. package/server/applications/sync/utils/routes.js.map +1 -1
  25. package/server/applications/users/guards/permissions.guard.js +4 -4
  26. package/server/applications/users/guards/permissions.guard.js.map +1 -1
  27. package/server/applications/users/guards/permissions.guard.spec.js +6 -6
  28. package/server/applications/users/guards/permissions.guard.spec.js.map +1 -1
  29. package/server/applications/users/guards/roles.guard.js +1 -1
  30. package/server/applications/users/guards/roles.guard.js.map +1 -1
  31. package/server/applications/users/models/user.model.js +1 -1
  32. package/server/applications/users/models/user.model.js.map +1 -1
  33. package/server/authentication/guards/auth-basic.guard.spec.js +38 -2
  34. package/server/authentication/guards/auth-basic.guard.spec.js.map +1 -1
  35. package/server/authentication/guards/auth-basic.strategy.js +0 -1
  36. package/server/authentication/guards/auth-basic.strategy.js.map +1 -1
  37. package/server/authentication/guards/auth-local.guard.spec.js +7 -5
  38. package/server/authentication/guards/auth-local.guard.spec.js.map +1 -1
  39. package/server/authentication/guards/auth-local.strategy.js +0 -1
  40. package/server/authentication/guards/auth-local.strategy.js.map +1 -1
  41. package/server/authentication/guards/auth-token-access.guard.spec.js +30 -0
  42. package/server/authentication/guards/auth-token-access.guard.spec.js.map +1 -1
  43. package/server/authentication/guards/auth-token-access.strategy.js +0 -1
  44. package/server/authentication/guards/auth-token-access.strategy.js.map +1 -1
  45. package/server/authentication/guards/auth-token-refresh.strategy.js +0 -1
  46. package/server/authentication/guards/auth-token-refresh.strategy.js.map +1 -1
  47. package/server/authentication/services/auth-methods/auth-method-database.service.js +1 -1
  48. package/server/authentication/services/auth-methods/auth-method-database.service.js.map +1 -1
  49. package/server/authentication/services/auth-methods/auth-method-database.service.spec.js +8 -6
  50. package/server/authentication/services/auth-methods/auth-method-database.service.spec.js.map +1 -1
  51. package/server/authentication/services/auth-methods/auth-method-ldap.service.js +2 -2
  52. package/server/authentication/services/auth-methods/auth-method-ldap.service.js.map +1 -1
  53. package/server/authentication/services/auth-methods/auth-method-ldap.service.spec.js +500 -5
  54. package/server/authentication/services/auth-methods/auth-method-ldap.service.spec.js.map +1 -1
  55. package/server/configuration/config.loader.js +0 -3
  56. package/server/configuration/config.loader.js.map +1 -1
  57. package/server/infrastructure/context/services/context-manager.service.spec.js +98 -0
  58. package/server/infrastructure/context/services/context-manager.service.spec.js.map +1 -0
  59. package/server/infrastructure/database/constants.js +0 -1
  60. package/server/infrastructure/database/constants.js.map +1 -1
  61. package/server/infrastructure/database/scripts/seed/usersgroups.js +3 -3
  62. package/server/infrastructure/database/scripts/seed/usersgroups.js.map +1 -1
  63. package/server/infrastructure/mailer/mailer.service.js +20 -19
  64. package/server/infrastructure/mailer/mailer.service.js.map +1 -1
  65. package/server/infrastructure/mailer/mailer.service.spec.js +176 -0
  66. package/server/infrastructure/mailer/mailer.service.spec.js.map +1 -0
  67. package/static/{chunk-KFM544CA.js → chunk-3GC2BQZD.js} +1 -1
  68. package/static/{chunk-MWFRZBJD.js → chunk-5YKWZT33.js} +1 -1
  69. package/static/{chunk-XUZSYWRF.js → chunk-6F55D74O.js} +1 -1
  70. package/static/{chunk-ZFKCGL6X.js → chunk-B2Y2RNFP.js} +1 -1
  71. package/static/{chunk-PYSFXLMV.js → chunk-C23BPTJZ.js} +1 -1
  72. package/static/{chunk-FJFNDK67.js → chunk-DGVNNICG.js} +1 -1
  73. package/static/{chunk-SRLMFJ7C.js → chunk-FQ4AFNGE.js} +1 -1
  74. package/static/{chunk-Z5X7LVMZ.js → chunk-GBCYYDCI.js} +1 -1
  75. package/static/{chunk-MK7WZG3F.js → chunk-HZA7R43P.js} +1 -1
  76. package/static/{chunk-HUWQHCUX.js → chunk-KZQCFEPT.js} +1 -1
  77. package/static/{chunk-S5WXHO6D.js → chunk-LJIGRUEF.js} +1 -1
  78. package/static/{chunk-4KESSWTF.js → chunk-MOVWEZ7J.js} +1 -1
  79. package/static/{chunk-LYTD6AJE.js → chunk-N2LYWNTC.js} +1 -1
  80. package/static/{chunk-NV2MEIWP.js → chunk-PF4K7MVG.js} +1 -1
  81. package/static/{chunk-SKDQM65G.js → chunk-PY3BGNJN.js} +1 -1
  82. package/static/{chunk-QTW62OKJ.js → chunk-T55FAU2O.js} +1 -1
  83. package/static/{chunk-N3T57OCA.js → chunk-TCFKH6K6.js} +1 -1
  84. package/static/{chunk-3FX6ISDY.js → chunk-TDQAEVZN.js} +1 -1
  85. package/static/{chunk-O4AQBQBF.js → chunk-TNW2CGK6.js} +1 -1
  86. package/static/{chunk-3S4WNZ2T.js → chunk-TXPODW5Q.js} +1 -1
  87. package/static/{chunk-BW5PQAKK.js → chunk-VHYIXL7R.js} +1 -1
  88. package/static/{chunk-CLSVDV7J.js → chunk-VMQMD36Z.js} +1 -1
  89. package/static/{chunk-O67RFAWU.js → chunk-VMUOUCEI.js} +1 -1
  90. package/static/{chunk-WLPYIJFI.js → chunk-W3QXNDI5.js} +1 -1
  91. package/static/{chunk-AY2GOSJ2.js → chunk-WHMS3PJ3.js} +1 -1
  92. package/static/{chunk-4TEHM3AS.js → chunk-WWIC7UW3.js} +1 -1
  93. package/static/{chunk-LB7B5RIV.js → chunk-X43VWRFP.js} +1 -1
  94. package/static/{chunk-WL65GYD5.js → chunk-Y2CDUS4J.js} +2 -2
  95. package/static/{chunk-ZTXJC5IC.js → chunk-YCINY2YI.js} +1 -1
  96. package/static/index.html +2 -2
  97. package/static/{main-RREKR34B.js → main-7LDKYVXO.js} +3 -3
  98. package/static/styles-FYUSO6OJ.css +1 -0
  99. package/static/styles-3DONJ2Z4.css +0 -1
@@ -7,29 +7,524 @@ Object.defineProperty(exports, "__esModule", {
7
7
  value: true
8
8
  });
9
9
  const _testing = require("@nestjs/testing");
10
+ const _appconstants = require("../../../app.constants");
10
11
  const _adminusersmanagerservice = require("../../../applications/users/services/admin-users-manager.service");
11
12
  const _usersmanagerservice = require("../../../applications/users/services/users-manager.service");
12
13
  const _authmethodldapservice = require("./auth-method-ldap.service");
14
+ const _configenvironment = require("../../../configuration/config.environment");
15
+ const _ldapts = require("ldapts");
16
+ const _functions = /*#__PURE__*/ _interop_require_wildcard(require("../../../common/functions"));
17
+ function _getRequireWildcardCache(nodeInterop) {
18
+ if (typeof WeakMap !== "function") return null;
19
+ var cacheBabelInterop = new WeakMap();
20
+ var cacheNodeInterop = new WeakMap();
21
+ return (_getRequireWildcardCache = function(nodeInterop) {
22
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
23
+ })(nodeInterop);
24
+ }
25
+ function _interop_require_wildcard(obj, nodeInterop) {
26
+ if (!nodeInterop && obj && obj.__esModule) {
27
+ return obj;
28
+ }
29
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
30
+ return {
31
+ default: obj
32
+ };
33
+ }
34
+ var cache = _getRequireWildcardCache(nodeInterop);
35
+ if (cache && cache.has(obj)) {
36
+ return cache.get(obj);
37
+ }
38
+ var newObj = {
39
+ __proto__: null
40
+ };
41
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
42
+ for(var key in obj){
43
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
44
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
45
+ if (desc && (desc.get || desc.set)) {
46
+ Object.defineProperty(newObj, key, desc);
47
+ } else {
48
+ newObj[key] = obj[key];
49
+ }
50
+ }
51
+ }
52
+ newObj.default = obj;
53
+ if (cache) {
54
+ cache.set(obj, newObj);
55
+ }
56
+ return newObj;
57
+ }
58
+ // Mock ldapts Client to simulate LDAP behaviors
59
+ jest.mock('ldapts', ()=>{
60
+ let InvalidCredentialsError = class InvalidCredentialsError extends Error {
61
+ };
62
+ const mockClientInstance = {
63
+ bind: jest.fn(),
64
+ search: jest.fn(),
65
+ unbind: jest.fn()
66
+ };
67
+ const Client = jest.fn().mockImplementation(()=>mockClientInstance);
68
+ return {
69
+ Client,
70
+ InvalidCredentialsError
71
+ };
72
+ });
73
+ // --- Test helpers (DRY) ---
74
+ // Reusable LDAP mocks
75
+ const mockBindResolve = (ldapClient)=>{
76
+ ldapClient.bind.mockResolvedValue(undefined);
77
+ ldapClient.unbind.mockResolvedValue(undefined);
78
+ };
79
+ const mockBindRejectInvalid = (ldapClient, InvalidCredentialsErrorCtor, message = 'invalid')=>{
80
+ ldapClient.bind.mockRejectedValue(new InvalidCredentialsErrorCtor(message));
81
+ ldapClient.unbind.mockResolvedValue(undefined);
82
+ };
83
+ const mockSearchEntries = (ldapClient, entries)=>{
84
+ ldapClient.search.mockResolvedValue({
85
+ searchEntries: entries
86
+ });
87
+ };
88
+ const mockSearchReject = (ldapClient, err)=>{
89
+ ldapClient.search.mockRejectedValue(err);
90
+ };
91
+ // User factory
92
+ const buildUser = (overrides = {})=>({
93
+ id: 0,
94
+ login: 'john',
95
+ email: 'old@example.org',
96
+ password: 'hashed',
97
+ isGuest: false,
98
+ isActive: true,
99
+ makePaths: jest.fn().mockResolvedValue(undefined),
100
+ ...overrides
101
+ });
102
+ // --------------------------
13
103
  describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
14
- let service;
104
+ let authMethodLdapService;
105
+ let usersManager;
106
+ let adminUsersManager;
107
+ const ldapClient = {
108
+ bind: jest.fn(),
109
+ search: jest.fn(),
110
+ unbind: jest.fn()
111
+ };
112
+ _ldapts.Client.mockImplementation(()=>ldapClient);
113
+ // Local helpers (need access to authMethodLdapService and ldapClient in this scope)
114
+ const setupLdapSuccess = (entries)=>{
115
+ mockBindResolve(ldapClient);
116
+ mockSearchEntries(ldapClient, entries);
117
+ };
118
+ const spyLoggerError = ()=>jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(()=>undefined);
15
119
  beforeAll(async ()=>{
16
120
  const module = await _testing.Test.createTestingModule({
17
121
  providers: [
18
122
  _authmethodldapservice.AuthMethodLdapService,
19
123
  {
20
124
  provide: _usersmanagerservice.UsersManager,
21
- useValue: {}
125
+ useValue: {
126
+ findUser: jest.fn(),
127
+ logUser: jest.fn(),
128
+ updateAccesses: jest.fn().mockResolvedValue(undefined)
129
+ }
22
130
  },
23
131
  {
24
132
  provide: _adminusersmanagerservice.AdminUsersManager,
25
- useValue: {}
133
+ useValue: {
134
+ createUserOrGuest: jest.fn(),
135
+ updateUserOrGuest: jest.fn()
136
+ }
26
137
  }
27
138
  ]
28
139
  }).compile();
29
- service = module.get(_authmethodldapservice.AuthMethodLdapService);
140
+ module.useLogger([
141
+ 'fatal'
142
+ ]);
143
+ authMethodLdapService = module.get(_authmethodldapservice.AuthMethodLdapService);
144
+ adminUsersManager = module.get(_adminusersmanagerservice.AdminUsersManager);
145
+ usersManager = module.get(_usersmanagerservice.UsersManager);
146
+ _configenvironment.configuration.auth.ldap = {
147
+ servers: [
148
+ 'ldap://localhost:389'
149
+ ],
150
+ loginAttribute: 'uid',
151
+ baseDN: 'ou=people,dc=example,dc=org',
152
+ filter: ''
153
+ };
30
154
  });
31
155
  it('should be defined', ()=>{
32
- expect(service).toBeDefined();
156
+ expect(authMethodLdapService).toBeDefined();
157
+ expect(usersManager).toBeDefined();
158
+ expect(adminUsersManager).toBeDefined();
159
+ expect(ldapClient).toBeDefined();
160
+ });
161
+ it('should authenticate a guest user via database and bypass LDAP', async ()=>{
162
+ // Arrange
163
+ const guestUser = {
164
+ id: 1,
165
+ login: 'guest1',
166
+ isGuest: true,
167
+ isActive: true
168
+ };
169
+ usersManager.findUser.mockResolvedValue(guestUser);
170
+ const dbAuthResult = {
171
+ ...guestUser,
172
+ token: 'jwt'
173
+ };
174
+ usersManager.logUser.mockResolvedValue(dbAuthResult);
175
+ const res = await authMethodLdapService.validateUser('guest1', 'pass', '127.0.0.1');
176
+ expect(res).toEqual(dbAuthResult);
177
+ expect(usersManager.logUser).toHaveBeenCalledWith(guestUser, 'pass', '127.0.0.1');
178
+ expect(_ldapts.Client).not.toHaveBeenCalled(); // client should not be constructed
179
+ });
180
+ it('should throw FORBIDDEN for locked account and LDAP login mismatch', async ()=>{
181
+ // Phase 1: locked account
182
+ usersManager.findUser.mockResolvedValue({
183
+ login: 'john',
184
+ isGuest: false,
185
+ isActive: false
186
+ });
187
+ const loggerErrorSpy1 = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(()=>undefined);
188
+ await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/account locked/i);
189
+ expect(loggerErrorSpy1).toHaveBeenCalled();
190
+ // Phase 2: mismatch between requested login and LDAP returned login
191
+ const existingUser = buildUser({
192
+ id: 8
193
+ });
194
+ usersManager.findUser.mockResolvedValue(existingUser);
195
+ const originalAttr = _configenvironment.configuration.auth.ldap.loginAttribute;
196
+ _configenvironment.configuration.auth.ldap.loginAttribute = 'cn';
197
+ mockBindResolve(ldapClient);
198
+ mockSearchEntries(ldapClient, [
199
+ {
200
+ uid: 'jane',
201
+ cn: 'john',
202
+ mail: 'jane@example.org'
203
+ }
204
+ ]);
205
+ await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/account matching error/i);
206
+ // restore the attribute to avoid impacting other tests
207
+ _configenvironment.configuration.auth.ldap.loginAttribute = originalAttr;
208
+ });
209
+ it('should handle invalid LDAP credentials for both existing and unknown users', async ()=>{
210
+ // Phase 1: existing user -> updateAccesses invoked with success=false and logger.error intercepted
211
+ const existingUser = buildUser({
212
+ id: 1
213
+ });
214
+ usersManager.findUser.mockResolvedValue(existingUser);
215
+ // Make LDAP bind throw InvalidCredentialsError
216
+ mockBindRejectInvalid(ldapClient, _ldapts.InvalidCredentialsError, 'invalid credentials');
217
+ // Force updateAccesses to reject to hit the catch and logger.error
218
+ const loggerErrorSpy = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(()=>undefined);
219
+ usersManager.updateAccesses.mockRejectedValueOnce(new Error('updateAccesses boom'));
220
+ const res1 = await authMethodLdapService.validateUser('john', 'badpwd', '10.0.0.1');
221
+ expect(res1).toBeNull();
222
+ expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '10.0.0.1', false);
223
+ expect(loggerErrorSpy).toHaveBeenCalled();
224
+ // Phase 2: unknown user → no access update
225
+ usersManager.updateAccesses.mockClear();
226
+ usersManager.findUser.mockResolvedValue(null);
227
+ ldapClient.bind.mockRejectedValue(new _ldapts.InvalidCredentialsError('invalid'));
228
+ ldapClient.unbind.mockResolvedValue(undefined);
229
+ const res2 = await authMethodLdapService.validateUser('jane', 'badpwd');
230
+ expect(res2).toBeNull();
231
+ expect(usersManager.updateAccesses).not.toHaveBeenCalled();
232
+ });
233
+ it('should handle LDAP new-user flow: missing fields, creation success, and multi-email selection', async ()=>{
234
+ // Phase 1: incomplete LDAP entry -> null + error log, no creation
235
+ usersManager.findUser.mockResolvedValue(null);
236
+ mockBindResolve(ldapClient);
237
+ // Simulate an entry with missing mail
238
+ mockSearchEntries(ldapClient, [
239
+ {
240
+ uid: 'jane',
241
+ cn: 'Jane Doe',
242
+ mail: undefined
243
+ }
244
+ ]);
245
+ const loggerErrorSpy = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(()=>undefined);
246
+ const resA = await authMethodLdapService.validateUser('jane', 'pwd');
247
+ expect(resA).toBeNull();
248
+ expect(adminUsersManager.createUserOrGuest).not.toHaveBeenCalled();
249
+ expect(loggerErrorSpy).toHaveBeenCalled();
250
+ // Phase 2: create a new user (success, single email)
251
+ usersManager.findUser.mockResolvedValue(null);
252
+ setupLdapSuccess([
253
+ {
254
+ uid: 'john',
255
+ cn: 'John Doe',
256
+ mail: 'john@example.org'
257
+ }
258
+ ]);
259
+ const createdUser = {
260
+ id: 2,
261
+ login: 'john',
262
+ isGuest: false,
263
+ isActive: true,
264
+ makePaths: jest.fn()
265
+ };
266
+ adminUsersManager.createUserOrGuest.mockResolvedValue(createdUser);
267
+ // Cover the success-flow catch branch
268
+ const loggerErrorSpy2 = spyLoggerError();
269
+ usersManager.updateAccesses.mockRejectedValueOnce(new Error('updateAccesses success flow boom'));
270
+ const resB = await authMethodLdapService.validateUser('john', 'pwd', '192.168.1.10');
271
+ expect(adminUsersManager.createUserOrGuest).toHaveBeenCalledWith({
272
+ login: 'john',
273
+ email: 'john@example.org',
274
+ password: 'pwd',
275
+ firstName: 'John',
276
+ lastName: 'Doe'
277
+ }, expect.anything() // USER_ROLE.USER
278
+ );
279
+ expect(resB).toBe(createdUser);
280
+ expect(usersManager.updateAccesses).toHaveBeenCalledWith(createdUser, '192.168.1.10', true);
281
+ expect(loggerErrorSpy2).toHaveBeenCalled();
282
+ // Phase 3: multiple emails -> keep the first
283
+ usersManager.findUser.mockResolvedValue(null);
284
+ setupLdapSuccess([
285
+ {
286
+ uid: 'multi',
287
+ cn: 'Multi Mail',
288
+ mail: [
289
+ 'first@example.org',
290
+ 'second@example.org'
291
+ ]
292
+ }
293
+ ]);
294
+ const createdUser2 = {
295
+ id: 9,
296
+ login: 'multi',
297
+ makePaths: jest.fn()
298
+ };
299
+ adminUsersManager.createUserOrGuest.mockResolvedValue(createdUser2);
300
+ const resC = await authMethodLdapService.validateUser('multi', 'pwd');
301
+ expect(adminUsersManager.createUserOrGuest).toHaveBeenCalledWith(expect.objectContaining({
302
+ email: 'first@example.org'
303
+ }), expect.anything());
304
+ expect(resC).toBe(createdUser2);
305
+ });
306
+ it('should update existing user profile when LDAP identity changed (except password assigned back)', async ()=>{
307
+ // Arrange: existing user with different profile and an old password
308
+ const existingUser = buildUser({
309
+ id: 5
310
+ });
311
+ usersManager.findUser.mockResolvedValue(existingUser);
312
+ // LDAP succeeds and returns different email and same uid
313
+ setupLdapSuccess([
314
+ {
315
+ uid: 'john',
316
+ cn: 'John Doe',
317
+ mail: 'john@example.org'
318
+ }
319
+ ]);
320
+ // Admin manager successfully updates a user
321
+ adminUsersManager.updateUserOrGuest.mockResolvedValue(undefined);
322
+ // Ensure password is considered changed so the update payload includes it,
323
+ // which then triggers the deletion and local assignment branches after update
324
+ const compareSpy = jest.spyOn(_functions, 'comparePassword').mockResolvedValue(false);
325
+ const res = await authMethodLdapService.validateUser('john', 'new-plain-password', '127.0.0.2');
326
+ expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalledWith(5, expect.objectContaining({
327
+ email: 'john@example.org',
328
+ firstName: 'John',
329
+ lastName: 'Doe'
330
+ }));
331
+ // Password should not be assigned back onto the user object (it is deleted before Object.assign)
332
+ expect(existingUser.password).toBe('hashed');
333
+ // Other fields should be updated locally
334
+ expect(existingUser.email).toBe('john@example.org');
335
+ expect(existingUser).toMatchObject({
336
+ firstName: 'John',
337
+ lastName: 'Doe'
338
+ });
339
+ // Accesses updated as success
340
+ expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '127.0.0.2', true);
341
+ // Returned user is the same instance
342
+ expect(res).toBe(existingUser);
343
+ // Second run: password unchanged (comparePassword => true) to cover the null branch for password
344
+ adminUsersManager.updateUserOrGuest.mockClear();
345
+ usersManager.updateAccesses.mockClear();
346
+ // Force another non-password change so an update occurs
347
+ existingUser.email = 'old@example.org';
348
+ compareSpy.mockResolvedValue(true);
349
+ const res2 = await authMethodLdapService.validateUser('john', 'same-plain-password', '127.0.0.3');
350
+ // Update should be called without password, only with changed fields
351
+ expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalled();
352
+ const updateArgs = adminUsersManager.updateUserOrGuest.mock.calls[0];
353
+ expect(updateArgs[0]).toBe(5);
354
+ expect(updateArgs[1]).toEqual(expect.objectContaining({
355
+ email: 'john@example.org'
356
+ }));
357
+ expect(updateArgs[1]).toEqual(expect.not.objectContaining({
358
+ password: expect.anything()
359
+ }));
360
+ // Password remains unchanged locally
361
+ expect(existingUser.password).toBe('hashed');
362
+ // Accesses updated as success
363
+ expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '127.0.0.3', true);
364
+ // Returned user is the same instance
365
+ expect(res2).toBe(existingUser);
366
+ // Third run: no changes at all (identityHasChanged is empty) to cover the else branch
367
+ adminUsersManager.updateUserOrGuest.mockClear();
368
+ usersManager.updateAccesses.mockClear();
369
+ compareSpy.mockResolvedValue(true);
370
+ // Local user already matches LDAP identity; call again
371
+ const res3 = await authMethodLdapService.validateUser('john', 'same-plain-password', '127.0.0.4');
372
+ // No update should be triggered
373
+ expect(adminUsersManager.updateUserOrGuest).not.toHaveBeenCalled();
374
+ // Access should still be updated as success
375
+ expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '127.0.0.4', true);
376
+ // Returned user is the same instance
377
+ expect(res3).toBe(existingUser);
378
+ });
379
+ it('should log failed access when LDAP search returns no entry or throws after bind', async ()=>{
380
+ // Phase 1: no entry found after a successful bind -> failed access
381
+ const existingUser = {
382
+ id: 7,
383
+ login: 'ghost',
384
+ isGuest: false,
385
+ isActive: true
386
+ };
387
+ usersManager.findUser.mockResolvedValue(existingUser);
388
+ setupLdapSuccess([]);
389
+ const resA = await authMethodLdapService.validateUser('ghost', 'pwd', '10.10.0.1');
390
+ expect(resA).toBeNull();
391
+ expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '10.10.0.1', false);
392
+ // Phase 2: exception during search after a bind -> failed access
393
+ jest.clearAllMocks();
394
+ const existingUser2 = {
395
+ id: 10,
396
+ login: 'john',
397
+ isGuest: false,
398
+ isActive: true
399
+ };
400
+ usersManager.findUser.mockResolvedValue(existingUser2);
401
+ mockBindResolve(ldapClient);
402
+ mockSearchReject(ldapClient, new Error('search failed'));
403
+ const resB = await authMethodLdapService.validateUser('john', 'pwd', '1.1.1.1');
404
+ expect(resB).toBeNull();
405
+ expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser2, '1.1.1.1', false);
406
+ });
407
+ it('should throw 500 when LDAP connection error occurs during bind', async ()=>{
408
+ // Arrange: no existing user to reach checkAuth flow
409
+ usersManager.findUser.mockResolvedValue(null);
410
+ const err1 = new Error('socket hang up');
411
+ const err2 = Object.assign(new Error('connect ECONNREFUSED'), {
412
+ code: Array.from(_appconstants.CONNECT_ERROR_CODE)[0]
413
+ });
414
+ ldapClient.bind.mockRejectedValue({
415
+ errors: [
416
+ err1,
417
+ err2
418
+ ]
419
+ });
420
+ ldapClient.unbind.mockResolvedValue(undefined);
421
+ // First scenario: recognized connection error -> throws 500
422
+ await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/authentication service/i);
423
+ // Second scenario: generic error (no code, not InvalidCredentialsError) -> resolves to null and no access update
424
+ ldapClient.bind.mockReset();
425
+ ldapClient.unbind.mockReset();
426
+ usersManager.updateAccesses.mockClear();
427
+ usersManager.findUser.mockResolvedValue(null);
428
+ ldapClient.bind.mockRejectedValue(new Error('unexpected failure'));
429
+ ldapClient.unbind.mockResolvedValue(undefined);
430
+ const res = await authMethodLdapService.validateUser('john', 'pwd');
431
+ expect(res).toBeNull();
432
+ expect(usersManager.updateAccesses).not.toHaveBeenCalled();
433
+ });
434
+ it('should log update failure and still call makePaths when updating existing user', async ()=>{
435
+ // Arrange: existing user with changed identity
436
+ const existingUser = buildUser({
437
+ id: 11,
438
+ email: 'old@ex.org'
439
+ });
440
+ usersManager.findUser.mockResolvedValue(existingUser);
441
+ // Ensure LDAP loginAttribute matches uid for this test (a previous test sets it to 'cn')
442
+ _configenvironment.configuration.auth.ldap.loginAttribute = 'uid';
443
+ setupLdapSuccess([
444
+ {
445
+ uid: 'john',
446
+ cn: 'John Doe',
447
+ mail: 'john@example.org'
448
+ }
449
+ ]);
450
+ adminUsersManager.updateUserOrGuest.mockRejectedValue(new Error('db error'));
451
+ // Force identity to be considered changed only for this test
452
+ jest.spyOn(_functions, 'comparePassword').mockResolvedValue(false);
453
+ jest.spyOn(_functions, 'splitFullName').mockReturnValue({
454
+ firstName: 'John',
455
+ lastName: 'Doe'
456
+ });
457
+ const res = await authMethodLdapService.validateUser('john', 'pwd');
458
+ expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalled();
459
+ // makePaths still invoked
460
+ expect(existingUser.makePaths).toHaveBeenCalled();
461
+ // Local fields unchanged since update failed
462
+ expect(existingUser.email).toBe('old@ex.org');
463
+ expect(res).toBe(existingUser);
464
+ });
465
+ it('should skip non-matching LDAP entries then update user with changed password without reassigning it', async ()=>{
466
+ // Phase A: LDAP returns an entry but loginAttribute value does not match -> checkAccess returns false (covers return after loop)
467
+ const userA = {
468
+ id: 20,
469
+ login: 'john',
470
+ isGuest: false,
471
+ isActive: true
472
+ };
473
+ usersManager.findUser.mockResolvedValue(userA);
474
+ _configenvironment.configuration.auth.ldap.loginAttribute = 'uid';
475
+ ldapClient.bind.mockResolvedValue(undefined);
476
+ // Non-matching entry: uid !== requested uid
477
+ ldapClient.search.mockResolvedValue({
478
+ searchEntries: [
479
+ {
480
+ uid: 'jane',
481
+ cn: 'Jane Doe',
482
+ mail: 'jane@example.org'
483
+ }
484
+ ]
485
+ });
486
+ ldapClient.unbind.mockResolvedValue(undefined);
487
+ const resA = await authMethodLdapService.validateUser('john', 'pwd', '3.3.3.3');
488
+ expect(resA).toBeNull();
489
+ expect(usersManager.updateAccesses).toHaveBeenCalledWith(userA, '3.3.3.3', false);
490
+ // Phase B: Matching entry + password considered changed -> updateUserOrGuest called, password not reassigned locally
491
+ jest.clearAllMocks();
492
+ const userB = buildUser({
493
+ id: 21,
494
+ email: 'old@ex.org'
495
+ });
496
+ usersManager.findUser.mockResolvedValue(userB);
497
+ _configenvironment.configuration.auth.ldap.loginAttribute = 'uid';
498
+ setupLdapSuccess([
499
+ {
500
+ uid: 'john',
501
+ cn: 'John Doe',
502
+ mail: 'john@example.org'
503
+ }
504
+ ]);
505
+ adminUsersManager.updateUserOrGuest.mockResolvedValue(undefined);
506
+ // Force password to be considered changed to execute deletion + Object.assign branch
507
+ jest.spyOn(_functions, 'comparePassword').mockResolvedValue(false);
508
+ jest.spyOn(_functions, 'splitFullName').mockReturnValue({
509
+ firstName: 'John',
510
+ lastName: 'Doe'
511
+ });
512
+ const resB = await authMethodLdapService.validateUser('john', 'newpwd', '4.4.4.4');
513
+ // Line 132: updateUserOrGuest call
514
+ expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalledWith(21, expect.objectContaining({
515
+ email: 'john@example.org',
516
+ firstName: 'John',
517
+ lastName: 'Doe'
518
+ }));
519
+ // Lines 139-142: password removed from local assign, other fields assigned
520
+ expect(userB.password).toBe('hashed');
521
+ expect(userB.email).toBe('john@example.org');
522
+ expect(userB).toMatchObject({
523
+ firstName: 'John',
524
+ lastName: 'Doe'
525
+ });
526
+ expect(userB.makePaths).toHaveBeenCalled();
527
+ expect(resB).toBe(userB);
33
528
  });
34
529
  });
35
530
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/authentication/services/auth-methods/auth-method-ldap.service.spec.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { Test, TestingModule } from '@nestjs/testing'\nimport { AdminUsersManager } from '../../../applications/users/services/admin-users-manager.service'\nimport { UsersManager } from '../../../applications/users/services/users-manager.service'\nimport { AuthMethodLdapService } from './auth-method-ldap.service'\n\ndescribe(AuthMethodLdapService.name, () => {\n let service: AuthMethodLdapService\n\n beforeAll(async () => {\n const module: TestingModule = await Test.createTestingModule({\n providers: [AuthMethodLdapService, { provide: UsersManager, useValue: {} }, { provide: AdminUsersManager, useValue: {} }]\n }).compile()\n\n service = module.get<AuthMethodLdapService>(AuthMethodLdapService)\n })\n\n it('should be defined', () => {\n expect(service).toBeDefined()\n })\n})\n"],"names":["describe","AuthMethodLdapService","name","service","beforeAll","module","Test","createTestingModule","providers","provide","UsersManager","useValue","AdminUsersManager","compile","get","it","expect","toBeDefined"],"mappings":"AAAA;;;;CAIC;;;;yBAEmC;0CACF;qCACL;uCACS;AAEtCA,SAASC,4CAAqB,CAACC,IAAI,EAAE;IACnC,IAAIC;IAEJC,UAAU;QACR,MAAMC,SAAwB,MAAMC,aAAI,CAACC,mBAAmB,CAAC;YAC3DC,WAAW;gBAACP,4CAAqB;gBAAE;oBAAEQ,SAASC,iCAAY;oBAAEC,UAAU,CAAC;gBAAE;gBAAG;oBAAEF,SAASG,2CAAiB;oBAAED,UAAU,CAAC;gBAAE;aAAE;QAC3H,GAAGE,OAAO;QAEVV,UAAUE,OAAOS,GAAG,CAAwBb,4CAAqB;IACnE;IAEAc,GAAG,qBAAqB;QACtBC,OAAOb,SAASc,WAAW;IAC7B;AACF"}
1
+ {"version":3,"sources":["../../../../../backend/src/authentication/services/auth-methods/auth-method-ldap.service.spec.ts"],"sourcesContent":["/*\n * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>\n * This file is part of Sync-in | The open source file sync and share solution\n * See the LICENSE file for licensing details\n */\n\nimport { Test, TestingModule } from '@nestjs/testing'\nimport { CONNECT_ERROR_CODE } from '../../../app.constants'\nimport { UserModel } from '../../../applications/users/models/user.model'\nimport { AdminUsersManager } from '../../../applications/users/services/admin-users-manager.service'\nimport { UsersManager } from '../../../applications/users/services/users-manager.service'\nimport { AuthMethodLdapService } from './auth-method-ldap.service'\nimport { configuration } from '../../../configuration/config.environment'\nimport { Client, InvalidCredentialsError } from 'ldapts'\nimport { Mocked } from 'jest-mock'\nimport * as commonFunctions from '../../../common/functions'\n\n// Mock ldapts Client to simulate LDAP behaviors\njest.mock('ldapts', () => {\n class InvalidCredentialsError extends Error {}\n const mockClientInstance = {\n bind: jest.fn(),\n search: jest.fn(),\n unbind: jest.fn()\n }\n const Client = jest.fn().mockImplementation(() => mockClientInstance)\n return { Client, InvalidCredentialsError }\n})\n\n// --- Test helpers (DRY) ---\n// Reusable LDAP mocks\nconst mockBindResolve = (ldapClient: any) => {\n ldapClient.bind.mockResolvedValue(undefined)\n ldapClient.unbind.mockResolvedValue(undefined)\n}\nconst mockBindRejectInvalid = (ldapClient: any, InvalidCredentialsErrorCtor: any, message = 'invalid') => {\n ldapClient.bind.mockRejectedValue(new InvalidCredentialsErrorCtor(message))\n ldapClient.unbind.mockResolvedValue(undefined)\n}\nconst mockSearchEntries = (ldapClient: any, entries: any[]) => {\n ldapClient.search.mockResolvedValue({ searchEntries: entries })\n}\nconst mockSearchReject = (ldapClient: any, err: Error) => {\n ldapClient.search.mockRejectedValue(err)\n}\n// User factory\nconst buildUser = (overrides: Partial<UserModel> = {}) =>\n ({\n id: 0,\n login: 'john',\n email: 'old@example.org',\n password: 'hashed',\n isGuest: false,\n isActive: true,\n makePaths: jest.fn().mockResolvedValue(undefined),\n ...overrides\n }) as any\n\n// --------------------------\n\ndescribe(AuthMethodLdapService.name, () => {\n let authMethodLdapService: AuthMethodLdapService\n let usersManager: Mocked<UsersManager>\n let adminUsersManager: Mocked<AdminUsersManager>\n const ldapClient = {\n bind: jest.fn(),\n search: jest.fn(),\n unbind: jest.fn()\n }\n ;(Client as Mocked<any>).mockImplementation(() => ldapClient)\n\n // Local helpers (need access to authMethodLdapService and ldapClient in this scope)\n const setupLdapSuccess = (entries: any[]) => {\n mockBindResolve(ldapClient)\n mockSearchEntries(ldapClient, entries)\n }\n const spyLoggerError = () => jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any)\n\n beforeAll(async () => {\n const module: TestingModule = await Test.createTestingModule({\n providers: [\n AuthMethodLdapService,\n {\n provide: UsersManager,\n useValue: {\n findUser: jest.fn(),\n logUser: jest.fn(),\n updateAccesses: jest.fn().mockResolvedValue(undefined)\n }\n },\n {\n provide: AdminUsersManager,\n useValue: {\n createUserOrGuest: jest.fn(),\n updateUserOrGuest: jest.fn()\n }\n }\n ]\n }).compile()\n\n module.useLogger(['fatal'])\n authMethodLdapService = module.get<AuthMethodLdapService>(AuthMethodLdapService)\n adminUsersManager = module.get<Mocked<AdminUsersManager>>(AdminUsersManager)\n usersManager = module.get<Mocked<UsersManager>>(UsersManager)\n configuration.auth.ldap = { servers: ['ldap://localhost:389'], loginAttribute: 'uid', baseDN: 'ou=people,dc=example,dc=org', filter: '' }\n })\n\n it('should be defined', () => {\n expect(authMethodLdapService).toBeDefined()\n expect(usersManager).toBeDefined()\n expect(adminUsersManager).toBeDefined()\n expect(ldapClient).toBeDefined()\n })\n\n it('should authenticate a guest user via database and bypass LDAP', async () => {\n // Arrange\n const guestUser: any = { id: 1, login: 'guest1', isGuest: true, isActive: true }\n usersManager.findUser.mockResolvedValue(guestUser)\n const dbAuthResult: any = { ...guestUser, token: 'jwt' }\n usersManager.logUser.mockResolvedValue(dbAuthResult)\n\n const res = await authMethodLdapService.validateUser('guest1', 'pass', '127.0.0.1')\n\n expect(res).toEqual(dbAuthResult)\n expect(usersManager.logUser).toHaveBeenCalledWith(guestUser, 'pass', '127.0.0.1')\n expect(Client).not.toHaveBeenCalled() // client should not be constructed\n })\n\n it('should throw FORBIDDEN for locked account and LDAP login mismatch', async () => {\n // Phase 1: locked account\n usersManager.findUser.mockResolvedValue({ login: 'john', isGuest: false, isActive: false } as UserModel)\n const loggerErrorSpy1 = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any)\n\n await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/account locked/i)\n expect(loggerErrorSpy1).toHaveBeenCalled()\n\n // Phase 2: mismatch between requested login and LDAP returned login\n const existingUser: any = buildUser({ id: 8 })\n usersManager.findUser.mockResolvedValue(existingUser)\n const originalAttr = configuration.auth.ldap.loginAttribute\n configuration.auth.ldap.loginAttribute = 'cn'\n mockBindResolve(ldapClient)\n mockSearchEntries(ldapClient, [{ uid: 'jane', cn: 'john', mail: 'jane@example.org' }])\n\n await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/account matching error/i)\n\n // restore the attribute to avoid impacting other tests\n configuration.auth.ldap.loginAttribute = originalAttr\n })\n\n it('should handle invalid LDAP credentials for both existing and unknown users', async () => {\n // Phase 1: existing user -> updateAccesses invoked with success=false and logger.error intercepted\n const existingUser: any = buildUser({ id: 1 })\n usersManager.findUser.mockResolvedValue(existingUser)\n // Make LDAP bind throw InvalidCredentialsError\n mockBindRejectInvalid(ldapClient, InvalidCredentialsError, 'invalid credentials')\n // Force updateAccesses to reject to hit the catch and logger.error\n const loggerErrorSpy = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any)\n usersManager.updateAccesses.mockRejectedValueOnce(new Error('updateAccesses boom'))\n\n const res1 = await authMethodLdapService.validateUser('john', 'badpwd', '10.0.0.1')\n\n expect(res1).toBeNull()\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '10.0.0.1', false)\n expect(loggerErrorSpy).toHaveBeenCalled()\n\n // Phase 2: unknown user → no access update\n usersManager.updateAccesses.mockClear()\n usersManager.findUser.mockResolvedValue(null)\n ldapClient.bind.mockRejectedValue(new InvalidCredentialsError('invalid'))\n ldapClient.unbind.mockResolvedValue(undefined)\n\n const res2 = await authMethodLdapService.validateUser('jane', 'badpwd')\n\n expect(res2).toBeNull()\n expect(usersManager.updateAccesses).not.toHaveBeenCalled()\n })\n\n it('should handle LDAP new-user flow: missing fields, creation success, and multi-email selection', async () => {\n // Phase 1: incomplete LDAP entry -> null + error log, no creation\n usersManager.findUser.mockResolvedValue(null)\n mockBindResolve(ldapClient)\n // Simulate an entry with missing mail\n mockSearchEntries(ldapClient, [{ uid: 'jane', cn: 'Jane Doe', mail: undefined }])\n const loggerErrorSpy = jest.spyOn(authMethodLdapService['logger'], 'error').mockImplementation(() => undefined as any)\n\n const resA = await authMethodLdapService.validateUser('jane', 'pwd')\n\n expect(resA).toBeNull()\n expect(adminUsersManager.createUserOrGuest).not.toHaveBeenCalled()\n expect(loggerErrorSpy).toHaveBeenCalled()\n\n // Phase 2: create a new user (success, single email)\n usersManager.findUser.mockResolvedValue(null)\n setupLdapSuccess([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }])\n\n const createdUser: any = { id: 2, login: 'john', isGuest: false, isActive: true, makePaths: jest.fn() }\n adminUsersManager.createUserOrGuest.mockResolvedValue(createdUser)\n // Cover the success-flow catch branch\n const loggerErrorSpy2 = spyLoggerError()\n usersManager.updateAccesses.mockRejectedValueOnce(new Error('updateAccesses success flow boom'))\n\n const resB = await authMethodLdapService.validateUser('john', 'pwd', '192.168.1.10')\n\n expect(adminUsersManager.createUserOrGuest).toHaveBeenCalledWith(\n { login: 'john', email: 'john@example.org', password: 'pwd', firstName: 'John', lastName: 'Doe' },\n expect.anything() // USER_ROLE.USER\n )\n expect(resB).toBe(createdUser)\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(createdUser, '192.168.1.10', true)\n expect(loggerErrorSpy2).toHaveBeenCalled()\n\n // Phase 3: multiple emails -> keep the first\n usersManager.findUser.mockResolvedValue(null)\n setupLdapSuccess([{ uid: 'multi', cn: 'Multi Mail', mail: ['first@example.org', 'second@example.org'] }])\n\n const createdUser2: any = { id: 9, login: 'multi', makePaths: jest.fn() }\n adminUsersManager.createUserOrGuest.mockResolvedValue(createdUser2)\n\n const resC = await authMethodLdapService.validateUser('multi', 'pwd')\n\n expect(adminUsersManager.createUserOrGuest).toHaveBeenCalledWith(expect.objectContaining({ email: 'first@example.org' }), expect.anything())\n expect(resC).toBe(createdUser2)\n })\n\n it('should update existing user profile when LDAP identity changed (except password assigned back)', async () => {\n // Arrange: existing user with different profile and an old password\n const existingUser: any = buildUser({ id: 5 })\n usersManager.findUser.mockResolvedValue(existingUser)\n\n // LDAP succeeds and returns different email and same uid\n setupLdapSuccess([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }])\n\n // Admin manager successfully updates a user\n adminUsersManager.updateUserOrGuest.mockResolvedValue(undefined)\n\n // Ensure password is considered changed so the update payload includes it,\n // which then triggers the deletion and local assignment branches after update\n const compareSpy = jest.spyOn(commonFunctions, 'comparePassword').mockResolvedValue(false)\n\n const res = await authMethodLdapService.validateUser('john', 'new-plain-password', '127.0.0.2')\n\n expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalledWith(\n 5,\n expect.objectContaining({\n email: 'john@example.org',\n firstName: 'John',\n lastName: 'Doe'\n })\n )\n // Password should not be assigned back onto the user object (it is deleted before Object.assign)\n expect(existingUser.password).toBe('hashed')\n // Other fields should be updated locally\n expect(existingUser.email).toBe('john@example.org')\n expect(existingUser).toMatchObject({ firstName: 'John', lastName: 'Doe' })\n // Accesses updated as success\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '127.0.0.2', true)\n // Returned user is the same instance\n expect(res).toBe(existingUser)\n\n // Second run: password unchanged (comparePassword => true) to cover the null branch for password\n adminUsersManager.updateUserOrGuest.mockClear()\n usersManager.updateAccesses.mockClear()\n // Force another non-password change so an update occurs\n existingUser.email = 'old@example.org'\n compareSpy.mockResolvedValue(true)\n\n const res2 = await authMethodLdapService.validateUser('john', 'same-plain-password', '127.0.0.3')\n\n // Update should be called without password, only with changed fields\n expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalled()\n const updateArgs = adminUsersManager.updateUserOrGuest.mock.calls[0]\n expect(updateArgs[0]).toBe(5)\n expect(updateArgs[1]).toEqual(\n expect.objectContaining({\n email: 'john@example.org'\n })\n )\n expect(updateArgs[1]).toEqual(expect.not.objectContaining({ password: expect.anything() }))\n\n // Password remains unchanged locally\n expect(existingUser.password).toBe('hashed')\n // Accesses updated as success\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '127.0.0.3', true)\n // Returned user is the same instance\n expect(res2).toBe(existingUser)\n\n // Third run: no changes at all (identityHasChanged is empty) to cover the else branch\n adminUsersManager.updateUserOrGuest.mockClear()\n usersManager.updateAccesses.mockClear()\n compareSpy.mockResolvedValue(true)\n\n // Local user already matches LDAP identity; call again\n const res3 = await authMethodLdapService.validateUser('john', 'same-plain-password', '127.0.0.4')\n\n // No update should be triggered\n expect(adminUsersManager.updateUserOrGuest).not.toHaveBeenCalled()\n // Access should still be updated as success\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '127.0.0.4', true)\n // Returned user is the same instance\n expect(res3).toBe(existingUser)\n })\n\n it('should log failed access when LDAP search returns no entry or throws after bind', async () => {\n // Phase 1: no entry found after a successful bind -> failed access\n const existingUser: any = { id: 7, login: 'ghost', isGuest: false, isActive: true }\n usersManager.findUser.mockResolvedValue(existingUser)\n setupLdapSuccess([])\n\n const resA = await authMethodLdapService.validateUser('ghost', 'pwd', '10.10.0.1')\n\n expect(resA).toBeNull()\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser, '10.10.0.1', false)\n\n // Phase 2: exception during search after a bind -> failed access\n jest.clearAllMocks()\n const existingUser2: any = { id: 10, login: 'john', isGuest: false, isActive: true }\n usersManager.findUser.mockResolvedValue(existingUser2)\n mockBindResolve(ldapClient)\n mockSearchReject(ldapClient, new Error('search failed'))\n\n const resB = await authMethodLdapService.validateUser('john', 'pwd', '1.1.1.1')\n\n expect(resB).toBeNull()\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(existingUser2, '1.1.1.1', false)\n })\n\n it('should throw 500 when LDAP connection error occurs during bind', async () => {\n // Arrange: no existing user to reach checkAuth flow\n usersManager.findUser.mockResolvedValue(null)\n const err1 = new Error('socket hang up')\n const err2 = Object.assign(new Error('connect ECONNREFUSED'), { code: Array.from(CONNECT_ERROR_CODE)[0] })\n ldapClient.bind.mockRejectedValue({ errors: [err1, err2] })\n ldapClient.unbind.mockResolvedValue(undefined)\n\n // First scenario: recognized connection error -> throws 500\n await expect(authMethodLdapService.validateUser('john', 'pwd')).rejects.toThrow(/authentication service/i)\n\n // Second scenario: generic error (no code, not InvalidCredentialsError) -> resolves to null and no access update\n ldapClient.bind.mockReset()\n ldapClient.unbind.mockReset()\n usersManager.updateAccesses.mockClear()\n usersManager.findUser.mockResolvedValue(null as any)\n ldapClient.bind.mockRejectedValue(new Error('unexpected failure'))\n ldapClient.unbind.mockResolvedValue(undefined)\n\n const res = await authMethodLdapService.validateUser('john', 'pwd')\n expect(res).toBeNull()\n expect(usersManager.updateAccesses).not.toHaveBeenCalled()\n })\n\n it('should log update failure and still call makePaths when updating existing user', async () => {\n // Arrange: existing user with changed identity\n const existingUser: any = buildUser({ id: 11, email: 'old@ex.org' })\n usersManager.findUser.mockResolvedValue(existingUser)\n\n // Ensure LDAP loginAttribute matches uid for this test (a previous test sets it to 'cn')\n configuration.auth.ldap.loginAttribute = 'uid'\n\n setupLdapSuccess([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }])\n adminUsersManager.updateUserOrGuest.mockRejectedValue(new Error('db error'))\n\n // Force identity to be considered changed only for this test\n jest.spyOn(commonFunctions, 'comparePassword').mockResolvedValue(false)\n jest.spyOn(commonFunctions, 'splitFullName').mockReturnValue({ firstName: 'John', lastName: 'Doe' })\n\n const res = await authMethodLdapService.validateUser('john', 'pwd')\n\n expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalled()\n // makePaths still invoked\n expect(existingUser.makePaths).toHaveBeenCalled()\n // Local fields unchanged since update failed\n expect(existingUser.email).toBe('old@ex.org')\n expect(res).toBe(existingUser)\n })\n\n it('should skip non-matching LDAP entries then update user with changed password without reassigning it', async () => {\n // Phase A: LDAP returns an entry but loginAttribute value does not match -> checkAccess returns false (covers return after loop)\n const userA: any = { id: 20, login: 'john', isGuest: false, isActive: true }\n usersManager.findUser.mockResolvedValue(userA)\n configuration.auth.ldap.loginAttribute = 'uid'\n ldapClient.bind.mockResolvedValue(undefined)\n // Non-matching entry: uid !== requested uid\n ldapClient.search.mockResolvedValue({ searchEntries: [{ uid: 'jane', cn: 'Jane Doe', mail: 'jane@example.org' }] })\n ldapClient.unbind.mockResolvedValue(undefined)\n\n const resA = await authMethodLdapService.validateUser('john', 'pwd', '3.3.3.3')\n expect(resA).toBeNull()\n expect(usersManager.updateAccesses).toHaveBeenCalledWith(userA, '3.3.3.3', false)\n\n // Phase B: Matching entry + password considered changed -> updateUserOrGuest called, password not reassigned locally\n jest.clearAllMocks()\n const userB: any = buildUser({ id: 21, email: 'old@ex.org' })\n usersManager.findUser.mockResolvedValue(userB)\n configuration.auth.ldap.loginAttribute = 'uid'\n setupLdapSuccess([{ uid: 'john', cn: 'John Doe', mail: 'john@example.org' }])\n adminUsersManager.updateUserOrGuest.mockResolvedValue(undefined)\n\n // Force password to be considered changed to execute deletion + Object.assign branch\n jest.spyOn(commonFunctions, 'comparePassword').mockResolvedValue(false)\n jest.spyOn(commonFunctions, 'splitFullName').mockReturnValue({ firstName: 'John', lastName: 'Doe' })\n\n const resB = await authMethodLdapService.validateUser('john', 'newpwd', '4.4.4.4')\n\n // Line 132: updateUserOrGuest call\n expect(adminUsersManager.updateUserOrGuest).toHaveBeenCalledWith(\n 21,\n expect.objectContaining({ email: 'john@example.org', firstName: 'John', lastName: 'Doe' })\n )\n // Lines 139-142: password removed from local assign, other fields assigned\n expect(userB.password).toBe('hashed')\n expect(userB.email).toBe('john@example.org')\n expect(userB).toMatchObject({ firstName: 'John', lastName: 'Doe' })\n expect(userB.makePaths).toHaveBeenCalled()\n expect(resB).toBe(userB)\n })\n})\n"],"names":["jest","mock","InvalidCredentialsError","Error","mockClientInstance","bind","fn","search","unbind","Client","mockImplementation","mockBindResolve","ldapClient","mockResolvedValue","undefined","mockBindRejectInvalid","InvalidCredentialsErrorCtor","message","mockRejectedValue","mockSearchEntries","entries","searchEntries","mockSearchReject","err","buildUser","overrides","id","login","email","password","isGuest","isActive","makePaths","describe","AuthMethodLdapService","name","authMethodLdapService","usersManager","adminUsersManager","setupLdapSuccess","spyLoggerError","spyOn","beforeAll","module","Test","createTestingModule","providers","provide","UsersManager","useValue","findUser","logUser","updateAccesses","AdminUsersManager","createUserOrGuest","updateUserOrGuest","compile","useLogger","get","configuration","auth","ldap","servers","loginAttribute","baseDN","filter","it","expect","toBeDefined","guestUser","dbAuthResult","token","res","validateUser","toEqual","toHaveBeenCalledWith","not","toHaveBeenCalled","loggerErrorSpy1","rejects","toThrow","existingUser","originalAttr","uid","cn","mail","loggerErrorSpy","mockRejectedValueOnce","res1","toBeNull","mockClear","res2","resA","createdUser","loggerErrorSpy2","resB","firstName","lastName","anything","toBe","createdUser2","resC","objectContaining","compareSpy","commonFunctions","toMatchObject","updateArgs","calls","res3","clearAllMocks","existingUser2","err1","err2","Object","assign","code","Array","from","CONNECT_ERROR_CODE","errors","mockReset","mockReturnValue","userA","userB"],"mappings":"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"}
@@ -101,9 +101,6 @@ function loadEnvFile(envPath, envFileName, throwIfMissing = false) {
101
101
  ];
102
102
  for (const envFilePath of candidates){
103
103
  if (_nodefs.default.existsSync(envFilePath) && _nodefs.default.lstatSync(envFilePath).isFile()) {
104
- if (envFileName === _configconstants.ENVIRONMENT_FILE_NAME) {
105
- console.log(`Load configuration → ${envFilePath}`);
106
- }
107
104
  return _jsyaml.load(_nodefs.default.readFileSync(envFilePath, 'utf8'));
108
105
  }
109
106
  }