@sync-in/server 1.6.1 → 1.7.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 (45) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/environment/environment.dist.yaml +8 -2
  3. package/package.json +1 -1
  4. package/server/authentication/auth.config.js +15 -0
  5. package/server/authentication/auth.config.js.map +1 -1
  6. package/server/authentication/constants/auth-ldap.js +2 -1
  7. package/server/authentication/constants/auth-ldap.js.map +1 -1
  8. package/server/authentication/guards/auth-basic.strategy.js +2 -0
  9. package/server/authentication/guards/auth-basic.strategy.js.map +1 -1
  10. package/server/authentication/guards/auth-local.strategy.js +2 -0
  11. package/server/authentication/guards/auth-local.strategy.js.map +1 -1
  12. package/server/authentication/services/auth-methods/auth-method-ldap.service.js +75 -32
  13. package/server/authentication/services/auth-methods/auth-method-ldap.service.js.map +1 -1
  14. package/server/authentication/services/auth-methods/auth-method-ldap.service.spec.js +2 -1
  15. package/server/authentication/services/auth-methods/auth-method-ldap.service.spec.js.map +1 -1
  16. package/static/{chunk-Y7EH7G5K.js → chunk-3GFGJYMK.js} +1 -1
  17. package/static/{chunk-7FUM3JGM.js → chunk-4YGJGZZZ.js} +1 -1
  18. package/static/{chunk-34MKICK5.js → chunk-5K7HEX3C.js} +1 -1
  19. package/static/{chunk-U34OZUZ7.js → chunk-5KLMS6A4.js} +1 -1
  20. package/static/{chunk-QQ6UQQBR.js → chunk-BB4G55KE.js} +1 -1
  21. package/static/{chunk-S75P2FFI.js → chunk-DJYJ66UF.js} +1 -1
  22. package/static/{chunk-AWQ2YTVC.js → chunk-EVIE5F2U.js} +1 -1
  23. package/static/{chunk-JUNZFADM.js → chunk-EWKSX76T.js} +1 -1
  24. package/static/{chunk-2I4CUFUA.js → chunk-FHLACA7V.js} +1 -1
  25. package/static/{chunk-EFKMBLRE.js → chunk-GCATNU55.js} +1 -1
  26. package/static/{chunk-5O3DIUU3.js → chunk-GYODPCIE.js} +1 -1
  27. package/static/{chunk-27YQB3TE.js → chunk-HZTFYLM5.js} +1 -1
  28. package/static/{chunk-JQ5FTO2M.js → chunk-JSUKJT6Z.js} +1 -1
  29. package/static/{chunk-DSOE3FEP.js → chunk-LTGFCQR7.js} +1 -1
  30. package/static/{chunk-JASU3CIH.js → chunk-LV3PYKWO.js} +1 -1
  31. package/static/{chunk-HCDLWTMW.js → chunk-N2WFNW6M.js} +1 -1
  32. package/static/{chunk-QJX6ITLW.js → chunk-OUTBJSMW.js} +1 -1
  33. package/static/{chunk-LUWQFIWR.js → chunk-PTGDOWV3.js} +1 -1
  34. package/static/{chunk-ZQQPUYLU.js → chunk-QNJFQVYI.js} +1 -1
  35. package/static/{chunk-LJUKI4SQ.js → chunk-RS2PX32L.js} +1 -1
  36. package/static/{chunk-S2HDY3OL.js → chunk-RSSWH3S2.js} +1 -1
  37. package/static/{chunk-7DN7ZAPU.js → chunk-SIPE37PA.js} +1 -1
  38. package/static/{chunk-FUFKVHPU.js → chunk-TKTCBDOG.js} +1 -1
  39. package/static/{chunk-Q7D6RN4N.js → chunk-V6K2N46L.js} +1 -1
  40. package/static/{chunk-2MTM6SWN.js → chunk-XLCCZSQL.js} +1 -1
  41. package/static/{chunk-6NMVZIIT.js → chunk-YPEH66GG.js} +1 -1
  42. package/static/{chunk-T3EYFSVZ.js → chunk-YPOIUQ57.js} +1 -1
  43. package/static/{chunk-7P27WBGC.js → chunk-ZKCFO2OA.js} +1 -1
  44. package/static/index.html +1 -1
  45. package/static/{main-7SQDDVMD.js → main-MZ7HWZXO.js} +2 -2
package/CHANGELOG.md CHANGED
@@ -1,4 +1,12 @@
1
1
 
2
+ ## [1.7.0](https://github.com/Sync-in/server/compare/v1.6.1...v1.7.0) (2025-10-09)
3
+
4
+
5
+ ### Features
6
+
7
+ * **backend:auth:** add `adminGroup` support and improve LDAP user role assignment ([9074145](https://github.com/Sync-in/server/commit/9074145c9c86e023c73e0a5522f87441356bb240))
8
+ * **backend:auth:** enhance LDAP authentication configuration with upnSuffix and netbiosName parameters ([5a5d623](https://github.com/Sync-in/server/commit/5a5d62317198d3c1164bc6f9efe6bdb50bfe25f7))
9
+
2
10
  ## [1.6.1](https://github.com/Sync-in/server/compare/v1.6.0...v1.6.1) (2025-10-09)
3
11
 
4
12
 
@@ -115,17 +115,23 @@ auth:
115
115
  ldap:
116
116
  # e.g: [ldap://localhost:389, ldaps://localhost:636] (array required)
117
117
  servers: []
118
- # baseDN: distinguished name ( e.g.ou=people,dc=ldap,dc=sync-in,dc=com)
118
+ # baseDN: distinguished name (e.g., ou=people,dc=ldap,dc=sync-in,dc=com)
119
119
  baseDN:
120
120
  # filter, e.g: (acl=admin)
121
121
  filter:
122
122
  attributes:
123
- # login attribute: `uid` | `sAMAccountName` | `userPrincipalName`
123
+ # login attribute: `uid` | `sAMAccountName` | `userPrincipalName`, used to authenticate the user
124
124
  # default: `uid`
125
125
  login: uid
126
126
  # email attribute: `mail` or `email`
127
127
  # default: `mail`
128
128
  email: mail
129
+ # adminGroup: The CN of a group containing Sync-in administrators (e.g., administrators)
130
+ adminGroup:
131
+ # upnSuffix: AD domain suffix used with `userPrincipalName` to build UPN-style logins (e.g., user@`sync-in.com`)
132
+ upnSuffix:
133
+ # netbiosName: NetBIOS domain name used with `sAMAccountName` to build legacy logins (e.g., `SYNC_IN`\user)
134
+ netbiosName:
129
135
  applications:
130
136
  files:
131
137
  # required
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sync-in/server",
3
- "version": "1.6.1",
3
+ "version": "1.7.0",
4
4
  "description": "The secure, open-source platform for file storage, sharing, collaboration, and sync",
5
5
  "author": {
6
6
  "name": "Johan Legrand",
@@ -230,6 +230,21 @@ _ts_decorate([
230
230
  (0, _classtransformer.Type)(()=>AuthMethodLdapAttributesConfig),
231
231
  _ts_metadata("design:type", typeof AuthMethodLdapAttributesConfig === "undefined" ? Object : AuthMethodLdapAttributesConfig)
232
232
  ], AuthMethodLdapConfig.prototype, "attributes", void 0);
233
+ _ts_decorate([
234
+ (0, _classvalidator.IsOptional)(),
235
+ (0, _classvalidator.IsString)(),
236
+ _ts_metadata("design:type", String)
237
+ ], AuthMethodLdapConfig.prototype, "adminGroup", void 0);
238
+ _ts_decorate([
239
+ (0, _classvalidator.IsOptional)(),
240
+ (0, _classvalidator.IsString)(),
241
+ _ts_metadata("design:type", String)
242
+ ], AuthMethodLdapConfig.prototype, "upnSuffix", void 0);
243
+ _ts_decorate([
244
+ (0, _classvalidator.IsOptional)(),
245
+ (0, _classvalidator.IsString)(),
246
+ _ts_metadata("design:type", String)
247
+ ], AuthMethodLdapConfig.prototype, "netbiosName", void 0);
233
248
  let AuthConfig = class AuthConfig {
234
249
  constructor(){
235
250
  this.method = 'mysql';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../backend/src/authentication/auth.config.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 { Exclude, Transform, Type } from 'class-transformer'\nimport {\n ArrayNotEmpty,\n IsArray,\n IsBoolean,\n IsDefined,\n IsEnum,\n IsIn,\n IsNotEmpty,\n IsNotEmptyObject,\n IsObject,\n IsOptional,\n IsString,\n ValidateIf,\n ValidateNested\n} from 'class-validator'\nimport { SERVER_NAME } from '../app.constants'\nimport { ACCESS_KEY, CSRF_KEY, REFRESH_KEY, WS_KEY } from './constants/auth'\nimport { LDAP_COMMON_ATTR, LDAP_LOGIN_ATTR } from './constants/auth-ldap'\n\nexport class AuthMfaTotpConfig {\n @IsBoolean()\n enabled = true\n\n @IsString()\n issuer = SERVER_NAME\n}\n\nexport class AuthMfaConfig {\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthMfaTotpConfig)\n totp: AuthMfaTotpConfig = new AuthMfaTotpConfig()\n}\n\nexport class AuthTokenAccessConfig {\n @Exclude({ toClassOnly: true })\n // force default name\n name = ACCESS_KEY\n\n @IsString()\n @IsNotEmpty()\n secret: string\n\n @IsString()\n @IsNotEmpty()\n expiration = '30m'\n}\n\nexport class AuthTokenRefreshConfig {\n @Exclude({ toClassOnly: true })\n // force default name\n name = REFRESH_KEY\n\n @IsString()\n @IsNotEmpty()\n secret: string\n\n @IsString()\n @IsNotEmpty()\n expiration = '4h'\n}\n\nexport class AuthTokenCsrfConfig extends AuthTokenRefreshConfig {\n @IsString()\n @IsNotEmpty()\n override name: string = CSRF_KEY\n}\n\nexport class AuthTokenWSConfig extends AuthTokenRefreshConfig {\n @IsString()\n @IsNotEmpty()\n override name: string = WS_KEY\n}\n\nexport class AuthTokenConfig {\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthTokenAccessConfig)\n access: AuthTokenAccessConfig\n\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthTokenRefreshConfig)\n refresh: AuthTokenRefreshConfig\n\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthTokenCsrfConfig)\n csrf: AuthTokenCsrfConfig\n\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthTokenWSConfig)\n ws: AuthTokenWSConfig\n}\n\nexport class AuthMethodLdapAttributesConfig {\n @IsOptional()\n @IsString()\n @Transform(({ value }) => value || LDAP_LOGIN_ATTR.UID)\n @IsEnum(LDAP_LOGIN_ATTR)\n login: LDAP_LOGIN_ATTR = LDAP_LOGIN_ATTR.UID\n\n @IsOptional()\n @IsString()\n @Transform(({ value }) => value || LDAP_COMMON_ATTR.MAIL)\n email: string = LDAP_COMMON_ATTR.MAIL\n}\n\nexport class AuthMethodLdapConfig {\n @Transform(({ value }) => (Array.isArray(value) ? value.filter((v: string) => Boolean(v)) : value))\n @ArrayNotEmpty()\n @IsArray()\n @IsString({ each: true })\n servers: string[]\n\n @IsString()\n @IsNotEmpty()\n baseDN: string\n\n @IsOptional()\n @IsString()\n filter?: string\n\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthMethodLdapAttributesConfig)\n attributes: AuthMethodLdapAttributesConfig = new AuthMethodLdapAttributesConfig()\n}\n\nexport class AuthConfig {\n @IsString()\n @IsIn(['mysql', 'ldap'])\n method: 'mysql' | 'ldap' = 'mysql'\n\n @IsOptional()\n @IsString()\n encryptionKey: string\n\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthMfaConfig)\n mfa: AuthMfaConfig = new AuthMfaConfig()\n\n @IsString()\n @IsIn(['lax', 'strict'])\n cookieSameSite: 'lax' | 'strict' = 'strict'\n\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthTokenConfig)\n token: AuthTokenConfig\n\n @ValidateIf((o: AuthConfig) => o.method === 'ldap')\n @IsDefined()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthMethodLdapConfig)\n ldap: AuthMethodLdapConfig\n}\n"],"names":["AuthConfig","AuthMethodLdapAttributesConfig","AuthMethodLdapConfig","AuthMfaConfig","AuthMfaTotpConfig","AuthTokenAccessConfig","AuthTokenConfig","AuthTokenCsrfConfig","AuthTokenRefreshConfig","AuthTokenWSConfig","enabled","issuer","SERVER_NAME","totp","name","ACCESS_KEY","expiration","toClassOnly","REFRESH_KEY","CSRF_KEY","WS_KEY","login","LDAP_LOGIN_ATTR","UID","email","LDAP_COMMON_ATTR","MAIL","value","attributes","Array","isArray","filter","v","Boolean","each","method","mfa","cookieSameSite","o"],"mappings":"AAAA;;;;CAIC;;;;;;;;;;;QAiJYA;eAAAA;;QApCAC;eAAAA;;QAaAC;eAAAA;;QA5FAC;eAAAA;;QARAC;eAAAA;;QAiBAC;eAAAA;;QAwCAC;eAAAA;;QAZAC;eAAAA;;QAdAC;eAAAA;;QAoBAC;eAAAA;;;kCAvE4B;gCAelC;8BACqB;sBAC8B;0BACR;;;;;;;;;;AAE3C,IAAA,AAAML,oBAAN,MAAMA;;aAEXM,UAAU;aAGVC,SAASC,yBAAW;;AACtB;;;;;;;AAEO,IAAA,AAAMT,gBAAN,MAAMA;;aAMXU,OAA0B,IAAIT;;AAChC;;;;;;oCAFcA;;;AAIP,IAAA,AAAMC,wBAAN,MAAMA;;aAEX,qBAAqB;QACrBS,OAAOC,gBAAU;aAQjBC,aAAa;;AACf;;;QAXaC,aAAa;;;;;;;;;;;;AAanB,IAAA,AAAMT,yBAAN,MAAMA;;aAEX,qBAAqB;QACrBM,OAAOI,iBAAW;aAQlBF,aAAa;;AACf;;;QAXaC,aAAa;;;;;;;;;;;;AAanB,IAAA,AAAMV,sBAAN,MAAMA,4BAA4BC;;QAAlC,qBAGIM,OAAeK,cAAQ;;AAClC;;;;;;AAEO,IAAA,AAAMV,oBAAN,MAAMA,0BAA0BD;;QAAhC,qBAGIM,OAAeM,YAAM;;AAChC;;;;;;AAEO,IAAA,AAAMd,kBAAN,MAAMA;AA4Bb;;;;;;oCAvBcD;;;;;;;;oCAOAG;;;;;;;;oCAOAD;;;;;;;;oCAOAE;;;AAIP,IAAA,AAAMR,iCAAN,MAAMA;;aAKXoB,QAAyBC,yBAAe,CAACC,GAAG;aAK5CC,QAAgBC,0BAAgB,CAACC,IAAI;;AACvC;;;;sCARc,EAAEC,KAAK,EAAE,GAAKA,SAASL,yBAAe,CAACC,GAAG;;;;;;;sCAM1C,EAAEI,KAAK,EAAE,GAAKA,SAASF,0BAAgB,CAACC,IAAI;;;AAInD,IAAA,AAAMxB,uBAAN,MAAMA;;aAoBX0B,aAA6C,IAAI3B;;AACnD;;sCApBc,EAAE0B,KAAK,EAAE,GAAME,MAAMC,OAAO,CAACH,SAASA,MAAMI,MAAM,CAAC,CAACC,IAAcC,QAAQD,MAAML;;;;QAGhFO,MAAM;;;;;;;;;;;;;;;;;;;oCAeNjC;;;AAIP,IAAA,AAAMD,aAAN,MAAMA;;aAGXmC,SAA2B;aAW3BC,MAAqB,IAAIjC;aAIzBkC,iBAAmC;;AAerC;;;;QA/BS;QAAS;;;;;;;;;;;;;;oCAWJlC;;;;;;QAIL;QAAO;;;;;;;;;oCAOFG;;;;qCAGCgC,IAAkBA,EAAEH,MAAM,KAAK;;;;oCAIhCjC"}
1
+ {"version":3,"sources":["../../../backend/src/authentication/auth.config.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 { Exclude, Transform, Type } from 'class-transformer'\nimport {\n ArrayNotEmpty,\n IsArray,\n IsBoolean,\n IsDefined,\n IsEnum,\n IsIn,\n IsNotEmpty,\n IsNotEmptyObject,\n IsObject,\n IsOptional,\n IsString,\n ValidateIf,\n ValidateNested\n} from 'class-validator'\nimport { SERVER_NAME } from '../app.constants'\nimport { ACCESS_KEY, CSRF_KEY, REFRESH_KEY, WS_KEY } from './constants/auth'\nimport { LDAP_COMMON_ATTR, LDAP_LOGIN_ATTR } from './constants/auth-ldap'\n\nexport class AuthMfaTotpConfig {\n @IsBoolean()\n enabled = true\n\n @IsString()\n issuer = SERVER_NAME\n}\n\nexport class AuthMfaConfig {\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthMfaTotpConfig)\n totp: AuthMfaTotpConfig = new AuthMfaTotpConfig()\n}\n\nexport class AuthTokenAccessConfig {\n @Exclude({ toClassOnly: true })\n // force default name\n name = ACCESS_KEY\n\n @IsString()\n @IsNotEmpty()\n secret: string\n\n @IsString()\n @IsNotEmpty()\n expiration = '30m'\n}\n\nexport class AuthTokenRefreshConfig {\n @Exclude({ toClassOnly: true })\n // force default name\n name = REFRESH_KEY\n\n @IsString()\n @IsNotEmpty()\n secret: string\n\n @IsString()\n @IsNotEmpty()\n expiration = '4h'\n}\n\nexport class AuthTokenCsrfConfig extends AuthTokenRefreshConfig {\n @IsString()\n @IsNotEmpty()\n override name: string = CSRF_KEY\n}\n\nexport class AuthTokenWSConfig extends AuthTokenRefreshConfig {\n @IsString()\n @IsNotEmpty()\n override name: string = WS_KEY\n}\n\nexport class AuthTokenConfig {\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthTokenAccessConfig)\n access: AuthTokenAccessConfig\n\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthTokenRefreshConfig)\n refresh: AuthTokenRefreshConfig\n\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthTokenCsrfConfig)\n csrf: AuthTokenCsrfConfig\n\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthTokenWSConfig)\n ws: AuthTokenWSConfig\n}\n\nexport class AuthMethodLdapAttributesConfig {\n @IsOptional()\n @IsString()\n @Transform(({ value }) => value || LDAP_LOGIN_ATTR.UID)\n @IsEnum(LDAP_LOGIN_ATTR)\n login: LDAP_LOGIN_ATTR = LDAP_LOGIN_ATTR.UID\n\n @IsOptional()\n @IsString()\n @Transform(({ value }) => value || LDAP_COMMON_ATTR.MAIL)\n email: string = LDAP_COMMON_ATTR.MAIL\n}\n\nexport class AuthMethodLdapConfig {\n @Transform(({ value }) => (Array.isArray(value) ? value.filter((v: string) => Boolean(v)) : value))\n @ArrayNotEmpty()\n @IsArray()\n @IsString({ each: true })\n servers: string[]\n\n @IsString()\n @IsNotEmpty()\n baseDN: string\n\n @IsOptional()\n @IsString()\n filter?: string\n\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthMethodLdapAttributesConfig)\n attributes: AuthMethodLdapAttributesConfig = new AuthMethodLdapAttributesConfig()\n\n @IsOptional()\n @IsString()\n adminGroup?: string\n\n @IsOptional()\n @IsString()\n upnSuffix?: string\n\n @IsOptional()\n @IsString()\n netbiosName?: string\n}\n\nexport class AuthConfig {\n @IsString()\n @IsIn(['mysql', 'ldap'])\n method: 'mysql' | 'ldap' = 'mysql'\n\n @IsOptional()\n @IsString()\n encryptionKey: string\n\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthMfaConfig)\n mfa: AuthMfaConfig = new AuthMfaConfig()\n\n @IsString()\n @IsIn(['lax', 'strict'])\n cookieSameSite: 'lax' | 'strict' = 'strict'\n\n @IsDefined()\n @IsNotEmptyObject()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthTokenConfig)\n token: AuthTokenConfig\n\n @ValidateIf((o: AuthConfig) => o.method === 'ldap')\n @IsDefined()\n @IsObject()\n @ValidateNested()\n @Type(() => AuthMethodLdapConfig)\n ldap: AuthMethodLdapConfig\n}\n"],"names":["AuthConfig","AuthMethodLdapAttributesConfig","AuthMethodLdapConfig","AuthMfaConfig","AuthMfaTotpConfig","AuthTokenAccessConfig","AuthTokenConfig","AuthTokenCsrfConfig","AuthTokenRefreshConfig","AuthTokenWSConfig","enabled","issuer","SERVER_NAME","totp","name","ACCESS_KEY","expiration","toClassOnly","REFRESH_KEY","CSRF_KEY","WS_KEY","login","LDAP_LOGIN_ATTR","UID","email","LDAP_COMMON_ATTR","MAIL","value","attributes","Array","isArray","filter","v","Boolean","each","method","mfa","cookieSameSite","o"],"mappings":"AAAA;;;;CAIC;;;;;;;;;;;QA6JYA;eAAAA;;QAhDAC;eAAAA;;QAaAC;eAAAA;;QA5FAC;eAAAA;;QARAC;eAAAA;;QAiBAC;eAAAA;;QAwCAC;eAAAA;;QAZAC;eAAAA;;QAdAC;eAAAA;;QAoBAC;eAAAA;;;kCAvE4B;gCAelC;8BACqB;sBAC8B;0BACR;;;;;;;;;;AAE3C,IAAA,AAAML,oBAAN,MAAMA;;aAEXM,UAAU;aAGVC,SAASC,yBAAW;;AACtB;;;;;;;AAEO,IAAA,AAAMT,gBAAN,MAAMA;;aAMXU,OAA0B,IAAIT;;AAChC;;;;;;oCAFcA;;;AAIP,IAAA,AAAMC,wBAAN,MAAMA;;aAEX,qBAAqB;QACrBS,OAAOC,gBAAU;aAQjBC,aAAa;;AACf;;;QAXaC,aAAa;;;;;;;;;;;;AAanB,IAAA,AAAMT,yBAAN,MAAMA;;aAEX,qBAAqB;QACrBM,OAAOI,iBAAW;aAQlBF,aAAa;;AACf;;;QAXaC,aAAa;;;;;;;;;;;;AAanB,IAAA,AAAMV,sBAAN,MAAMA,4BAA4BC;;QAAlC,qBAGIM,OAAeK,cAAQ;;AAClC;;;;;;AAEO,IAAA,AAAMV,oBAAN,MAAMA,0BAA0BD;;QAAhC,qBAGIM,OAAeM,YAAM;;AAChC;;;;;;AAEO,IAAA,AAAMd,kBAAN,MAAMA;AA4Bb;;;;;;oCAvBcD;;;;;;;;oCAOAG;;;;;;;;oCAOAD;;;;;;;;oCAOAE;;;AAIP,IAAA,AAAMR,iCAAN,MAAMA;;aAKXoB,QAAyBC,yBAAe,CAACC,GAAG;aAK5CC,QAAgBC,0BAAgB,CAACC,IAAI;;AACvC;;;;sCARc,EAAEC,KAAK,EAAE,GAAKA,SAASL,yBAAe,CAACC,GAAG;;;;;;;sCAM1C,EAAEI,KAAK,EAAE,GAAKA,SAASF,0BAAgB,CAACC,IAAI;;;AAInD,IAAA,AAAMxB,uBAAN,MAAMA;;aAoBX0B,aAA6C,IAAI3B;;AAanD;;sCAhCc,EAAE0B,KAAK,EAAE,GAAME,MAAMC,OAAO,CAACH,SAASA,MAAMI,MAAM,CAAC,CAACC,IAAcC,QAAQD,MAAML;;;;QAGhFO,MAAM;;;;;;;;;;;;;;;;;;;oCAeNjC;;;;;;;;;;;;;;;;;;AAgBP,IAAA,AAAMD,aAAN,MAAMA;;aAGXmC,SAA2B;aAW3BC,MAAqB,IAAIjC;aAIzBkC,iBAAmC;;AAerC;;;;QA/BS;QAAS;;;;;;;;;;;;;;oCAWJlC;;;;;;QAIL;QAAO;;;;;;;;;oCAOFG;;;;qCAGCgC,IAAkBA,EAAEH,MAAM,KAAK;;;;oCAIhCjC"}
@@ -34,7 +34,8 @@ const LDAP_COMMON_ATTR = {
34
34
  GIVEN_NAME: 'givenName',
35
35
  SN: 'sn',
36
36
  CN: 'cn',
37
- DISPLAY_NAME: 'displayName'
37
+ DISPLAY_NAME: 'displayName',
38
+ MEMBER_OF: 'memberOf'
38
39
  };
39
40
  const ALL_LDAP_ATTRIBUTES = [
40
41
  ...Object.values(LDAP_LOGIN_ATTR),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../backend/src/authentication/constants/auth-ldap.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\nexport enum LDAP_LOGIN_ATTR {\n UID = 'uid',\n SAM = 'sAMAccountName',\n UPN = 'userPrincipalName'\n}\n\nexport const LDAP_COMMON_ATTR = {\n MAIL: 'mail',\n GIVEN_NAME: 'givenName',\n SN: 'sn',\n CN: 'cn',\n DISPLAY_NAME: 'displayName'\n} as const\n\nexport const ALL_LDAP_ATTRIBUTES = [...Object.values(LDAP_LOGIN_ATTR), ...Object.values(LDAP_COMMON_ATTR)]\n"],"names":["ALL_LDAP_ATTRIBUTES","LDAP_COMMON_ATTR","LDAP_LOGIN_ATTR","MAIL","GIVEN_NAME","SN","CN","DISPLAY_NAME","Object","values"],"mappings":"AAAA;;;;CAIC;;;;;;;;;;;QAgBYA;eAAAA;;QARAC;eAAAA;;QANDC;eAAAA;;;AAAL,IAAA,AAAKA,yCAAAA;;;;WAAAA;;AAML,MAAMD,mBAAmB;IAC9BE,MAAM;IACNC,YAAY;IACZC,IAAI;IACJC,IAAI;IACJC,cAAc;AAChB;AAEO,MAAMP,sBAAsB;OAAIQ,OAAOC,MAAM,CAACP;OAAqBM,OAAOC,MAAM,CAACR;CAAkB"}
1
+ {"version":3,"sources":["../../../../backend/src/authentication/constants/auth-ldap.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\nexport enum LDAP_LOGIN_ATTR {\n UID = 'uid',\n SAM = 'sAMAccountName',\n UPN = 'userPrincipalName'\n}\n\nexport const LDAP_COMMON_ATTR = {\n MAIL: 'mail',\n GIVEN_NAME: 'givenName',\n SN: 'sn',\n CN: 'cn',\n DISPLAY_NAME: 'displayName',\n MEMBER_OF: 'memberOf'\n} as const\n\nexport const ALL_LDAP_ATTRIBUTES = [...Object.values(LDAP_LOGIN_ATTR), ...Object.values(LDAP_COMMON_ATTR)]\n"],"names":["ALL_LDAP_ATTRIBUTES","LDAP_COMMON_ATTR","LDAP_LOGIN_ATTR","MAIL","GIVEN_NAME","SN","CN","DISPLAY_NAME","MEMBER_OF","Object","values"],"mappings":"AAAA;;;;CAIC;;;;;;;;;;;QAiBYA;eAAAA;;QATAC;eAAAA;;QANDC;eAAAA;;;AAAL,IAAA,AAAKA,yCAAAA;;;;WAAAA;;AAML,MAAMD,mBAAmB;IAC9BE,MAAM;IACNC,YAAY;IACZC,IAAI;IACJC,IAAI;IACJC,cAAc;IACdC,WAAW;AACb;AAEO,MAAMR,sBAAsB;OAAIS,OAAOC,MAAM,CAACR;OAAqBO,OAAOC,MAAM,CAACT;CAAkB"}
@@ -33,6 +33,8 @@ function _ts_metadata(k, v) {
33
33
  }
34
34
  let AuthBasicStrategy = class AuthBasicStrategy extends (0, _passport.PassportStrategy)(_passporthttp.BasicStrategy, 'basic') {
35
35
  async validate(req, loginOrEmail, password) {
36
+ loginOrEmail = loginOrEmail.trim();
37
+ password = password.trim();
36
38
  this.logger.assign({
37
39
  user: loginOrEmail
38
40
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../backend/src/authentication/guards/auth-basic.strategy.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 { Injectable } from '@nestjs/common'\nimport { AbstractStrategy, PassportStrategy } from '@nestjs/passport'\nimport { instanceToPlain, plainToInstance } from 'class-transformer'\nimport { FastifyRequest } from 'fastify'\nimport { PinoLogger } from 'nestjs-pino'\nimport { BasicStrategy } from 'passport-http'\nimport { SERVER_NAME } from '../../app.constants'\nimport { UserModel } from '../../applications/users/models/user.model'\nimport { Cache } from '../../infrastructure/cache/services/cache.service'\nimport { AUTH_SCOPE } from '../constants/scope'\nimport { AuthMethod } from '../models/auth-method'\n\n@Injectable()\nexport class AuthBasicStrategy extends PassportStrategy(BasicStrategy, 'basic') implements AbstractStrategy {\n private readonly CACHE_TTL = 900\n private readonly CACHE_KEY_PREFIX = 'auth-webdav'\n\n constructor(\n private readonly authMethod: AuthMethod,\n private readonly cache: Cache,\n private readonly logger: PinoLogger\n ) {\n super({ passReqToCallback: true, realm: SERVER_NAME })\n }\n\n async validate(req: FastifyRequest, loginOrEmail: string, password: string): Promise<Omit<UserModel, 'password'> | null> {\n this.logger.assign({ user: loginOrEmail })\n const authBasicUser = `${this.CACHE_KEY_PREFIX}-${req.headers['authorization'].split(' ').at(-1).toLowerCase()}`\n const userFromCache: any = await this.cache.get(authBasicUser)\n if (userFromCache === null) {\n // not authorized\n return null\n }\n if (userFromCache !== undefined) {\n // cached\n // warning: plainToInstance do not use constructor to instantiate class\n return plainToInstance(UserModel, userFromCache)\n }\n const userFromDB: UserModel = await this.authMethod.validateUser(loginOrEmail, password, req.ip, AUTH_SCOPE.WEBDAV)\n if (userFromDB !== null) {\n userFromDB.removePassword()\n }\n const userToCache: Record<string, any> | null = userFromDB ? instanceToPlain(userFromDB, { excludePrefixes: ['_'] }) : null\n this.cache.set(authBasicUser, userToCache, this.CACHE_TTL).catch((e: Error) => this.logger.error(`${this.validate.name} - ${e}`))\n return userFromDB\n }\n}\n"],"names":["AuthBasicStrategy","PassportStrategy","BasicStrategy","validate","req","loginOrEmail","password","logger","assign","user","authBasicUser","CACHE_KEY_PREFIX","headers","split","at","toLowerCase","userFromCache","cache","get","undefined","plainToInstance","UserModel","userFromDB","authMethod","validateUser","ip","AUTH_SCOPE","WEBDAV","removePassword","userToCache","instanceToPlain","excludePrefixes","set","CACHE_TTL","catch","e","error","name","passReqToCallback","realm","SERVER_NAME"],"mappings":"AAAA;;;;CAIC;;;;+BAeYA;;;eAAAA;;;wBAbc;0BACwB;kCACF;4BAEtB;8BACG;8BACF;2BACF;8BACJ;uBACK;4BACA;;;;;;;;;;AAGpB,IAAA,AAAMA,oBAAN,MAAMA,0BAA0BC,IAAAA,0BAAgB,EAACC,2BAAa,EAAE;IAYrE,MAAMC,SAASC,GAAmB,EAAEC,YAAoB,EAAEC,QAAgB,EAA+C;QACvH,IAAI,CAACC,MAAM,CAACC,MAAM,CAAC;YAAEC,MAAMJ;QAAa;QACxC,MAAMK,gBAAgB,GAAG,IAAI,CAACC,gBAAgB,CAAC,CAAC,EAAEP,IAAIQ,OAAO,CAAC,gBAAgB,CAACC,KAAK,CAAC,KAAKC,EAAE,CAAC,CAAC,GAAGC,WAAW,IAAI;QAChH,MAAMC,gBAAqB,MAAM,IAAI,CAACC,KAAK,CAACC,GAAG,CAACR;QAChD,IAAIM,kBAAkB,MAAM;YAC1B,iBAAiB;YACjB,OAAO;QACT;QACA,IAAIA,kBAAkBG,WAAW;YAC/B,SAAS;YACT,uEAAuE;YACvE,OAAOC,IAAAA,iCAAe,EAACC,oBAAS,EAAEL;QACpC;QACA,MAAMM,aAAwB,MAAM,IAAI,CAACC,UAAU,CAACC,YAAY,CAACnB,cAAcC,UAAUF,IAAIqB,EAAE,EAAEC,iBAAU,CAACC,MAAM;QAClH,IAAIL,eAAe,MAAM;YACvBA,WAAWM,cAAc;QAC3B;QACA,MAAMC,cAA0CP,aAAaQ,IAAAA,iCAAe,EAACR,YAAY;YAAES,iBAAiB;gBAAC;aAAI;QAAC,KAAK;QACvH,IAAI,CAACd,KAAK,CAACe,GAAG,CAACtB,eAAemB,aAAa,IAAI,CAACI,SAAS,EAAEC,KAAK,CAAC,CAACC,IAAa,IAAI,CAAC5B,MAAM,CAAC6B,KAAK,CAAC,GAAG,IAAI,CAACjC,QAAQ,CAACkC,IAAI,CAAC,GAAG,EAAEF,GAAG;QAC/H,OAAOb;IACT;IA5BA,YACE,AAAiBC,UAAsB,EACvC,AAAiBN,KAAY,EAC7B,AAAiBV,MAAkB,CACnC;QACA,KAAK,CAAC;YAAE+B,mBAAmB;YAAMC,OAAOC,yBAAW;QAAC,SAJnCjB,aAAAA,iBACAN,QAAAA,YACAV,SAAAA,aANF0B,YAAY,UACZtB,mBAAmB;IAQpC;AAuBF"}
1
+ {"version":3,"sources":["../../../../backend/src/authentication/guards/auth-basic.strategy.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 { Injectable } from '@nestjs/common'\nimport { AbstractStrategy, PassportStrategy } from '@nestjs/passport'\nimport { instanceToPlain, plainToInstance } from 'class-transformer'\nimport { FastifyRequest } from 'fastify'\nimport { PinoLogger } from 'nestjs-pino'\nimport { BasicStrategy } from 'passport-http'\nimport { SERVER_NAME } from '../../app.constants'\nimport { UserModel } from '../../applications/users/models/user.model'\nimport { Cache } from '../../infrastructure/cache/services/cache.service'\nimport { AUTH_SCOPE } from '../constants/scope'\nimport { AuthMethod } from '../models/auth-method'\n\n@Injectable()\nexport class AuthBasicStrategy extends PassportStrategy(BasicStrategy, 'basic') implements AbstractStrategy {\n private readonly CACHE_TTL = 900\n private readonly CACHE_KEY_PREFIX = 'auth-webdav'\n\n constructor(\n private readonly authMethod: AuthMethod,\n private readonly cache: Cache,\n private readonly logger: PinoLogger\n ) {\n super({ passReqToCallback: true, realm: SERVER_NAME })\n }\n\n async validate(req: FastifyRequest, loginOrEmail: string, password: string): Promise<Omit<UserModel, 'password'> | null> {\n loginOrEmail = loginOrEmail.trim()\n password = password.trim()\n this.logger.assign({ user: loginOrEmail })\n const authBasicUser = `${this.CACHE_KEY_PREFIX}-${req.headers['authorization'].split(' ').at(-1).toLowerCase()}`\n const userFromCache: any = await this.cache.get(authBasicUser)\n if (userFromCache === null) {\n // not authorized\n return null\n }\n if (userFromCache !== undefined) {\n // cached\n // warning: plainToInstance do not use constructor to instantiate class\n return plainToInstance(UserModel, userFromCache)\n }\n const userFromDB: UserModel = await this.authMethod.validateUser(loginOrEmail, password, req.ip, AUTH_SCOPE.WEBDAV)\n if (userFromDB !== null) {\n userFromDB.removePassword()\n }\n const userToCache: Record<string, any> | null = userFromDB ? instanceToPlain(userFromDB, { excludePrefixes: ['_'] }) : null\n this.cache.set(authBasicUser, userToCache, this.CACHE_TTL).catch((e: Error) => this.logger.error(`${this.validate.name} - ${e}`))\n return userFromDB\n }\n}\n"],"names":["AuthBasicStrategy","PassportStrategy","BasicStrategy","validate","req","loginOrEmail","password","trim","logger","assign","user","authBasicUser","CACHE_KEY_PREFIX","headers","split","at","toLowerCase","userFromCache","cache","get","undefined","plainToInstance","UserModel","userFromDB","authMethod","validateUser","ip","AUTH_SCOPE","WEBDAV","removePassword","userToCache","instanceToPlain","excludePrefixes","set","CACHE_TTL","catch","e","error","name","passReqToCallback","realm","SERVER_NAME"],"mappings":"AAAA;;;;CAIC;;;;+BAeYA;;;eAAAA;;;wBAbc;0BACwB;kCACF;4BAEtB;8BACG;8BACF;2BACF;8BACJ;uBACK;4BACA;;;;;;;;;;AAGpB,IAAA,AAAMA,oBAAN,MAAMA,0BAA0BC,IAAAA,0BAAgB,EAACC,2BAAa,EAAE;IAYrE,MAAMC,SAASC,GAAmB,EAAEC,YAAoB,EAAEC,QAAgB,EAA+C;QACvHD,eAAeA,aAAaE,IAAI;QAChCD,WAAWA,SAASC,IAAI;QACxB,IAAI,CAACC,MAAM,CAACC,MAAM,CAAC;YAAEC,MAAML;QAAa;QACxC,MAAMM,gBAAgB,GAAG,IAAI,CAACC,gBAAgB,CAAC,CAAC,EAAER,IAAIS,OAAO,CAAC,gBAAgB,CAACC,KAAK,CAAC,KAAKC,EAAE,CAAC,CAAC,GAAGC,WAAW,IAAI;QAChH,MAAMC,gBAAqB,MAAM,IAAI,CAACC,KAAK,CAACC,GAAG,CAACR;QAChD,IAAIM,kBAAkB,MAAM;YAC1B,iBAAiB;YACjB,OAAO;QACT;QACA,IAAIA,kBAAkBG,WAAW;YAC/B,SAAS;YACT,uEAAuE;YACvE,OAAOC,IAAAA,iCAAe,EAACC,oBAAS,EAAEL;QACpC;QACA,MAAMM,aAAwB,MAAM,IAAI,CAACC,UAAU,CAACC,YAAY,CAACpB,cAAcC,UAAUF,IAAIsB,EAAE,EAAEC,iBAAU,CAACC,MAAM;QAClH,IAAIL,eAAe,MAAM;YACvBA,WAAWM,cAAc;QAC3B;QACA,MAAMC,cAA0CP,aAAaQ,IAAAA,iCAAe,EAACR,YAAY;YAAES,iBAAiB;gBAAC;aAAI;QAAC,KAAK;QACvH,IAAI,CAACd,KAAK,CAACe,GAAG,CAACtB,eAAemB,aAAa,IAAI,CAACI,SAAS,EAAEC,KAAK,CAAC,CAACC,IAAa,IAAI,CAAC5B,MAAM,CAAC6B,KAAK,CAAC,GAAG,IAAI,CAAClC,QAAQ,CAACmC,IAAI,CAAC,GAAG,EAAEF,GAAG;QAC/H,OAAOb;IACT;IA9BA,YACE,AAAiBC,UAAsB,EACvC,AAAiBN,KAAY,EAC7B,AAAiBV,MAAkB,CACnC;QACA,KAAK,CAAC;YAAE+B,mBAAmB;YAAMC,OAAOC,yBAAW;QAAC,SAJnCjB,aAAAA,iBACAN,QAAAA,YACAV,SAAAA,aANF0B,YAAY,UACZtB,mBAAmB;IAQpC;AAyBF"}
@@ -28,6 +28,8 @@ function _ts_metadata(k, v) {
28
28
  }
29
29
  let AuthLocalStrategy = class AuthLocalStrategy extends (0, _passport.PassportStrategy)(_passportlocal.Strategy, 'local') {
30
30
  async validate(req, loginOrEmail, password) {
31
+ loginOrEmail = loginOrEmail.trim();
32
+ password = password.trim();
31
33
  this.logger.assign({
32
34
  user: loginOrEmail
33
35
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../backend/src/authentication/guards/auth-local.strategy.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 { Injectable, UnauthorizedException } from '@nestjs/common'\nimport { PassportStrategy, AbstractStrategy } from '@nestjs/passport'\nimport type { FastifyRequest } from 'fastify'\nimport { PinoLogger } from 'nestjs-pino'\nimport { Strategy } from 'passport-local'\nimport type { UserModel } from '../../applications/users/models/user.model'\nimport { AuthMethod } from '../models/auth-method'\n\n@Injectable()\nexport class AuthLocalStrategy extends PassportStrategy(Strategy, 'local') implements AbstractStrategy {\n constructor(\n private readonly authMethod: AuthMethod,\n private readonly logger: PinoLogger\n ) {\n super({ usernameField: 'login', passwordField: 'password', passReqToCallback: true })\n }\n\n async validate(req: FastifyRequest, loginOrEmail: string, password: string): Promise<UserModel> {\n this.logger.assign({ user: loginOrEmail })\n const user: UserModel = await this.authMethod.validateUser(loginOrEmail, password, req.ip)\n if (user) {\n user.removePassword()\n return user\n }\n throw new UnauthorizedException('Wrong login or password')\n }\n}\n"],"names":["AuthLocalStrategy","PassportStrategy","Strategy","validate","req","loginOrEmail","password","logger","assign","user","authMethod","validateUser","ip","removePassword","UnauthorizedException","usernameField","passwordField","passReqToCallback"],"mappings":"AAAA;;;;CAIC;;;;+BAWYA;;;eAAAA;;;wBATqC;0BACC;4BAExB;+BACF;4BAEE;;;;;;;;;;AAGpB,IAAA,AAAMA,oBAAN,MAAMA,0BAA0BC,IAAAA,0BAAgB,EAACC,uBAAQ,EAAE;IAQhE,MAAMC,SAASC,GAAmB,EAAEC,YAAoB,EAAEC,QAAgB,EAAsB;QAC9F,IAAI,CAACC,MAAM,CAACC,MAAM,CAAC;YAAEC,MAAMJ;QAAa;QACxC,MAAMI,OAAkB,MAAM,IAAI,CAACC,UAAU,CAACC,YAAY,CAACN,cAAcC,UAAUF,IAAIQ,EAAE;QACzF,IAAIH,MAAM;YACRA,KAAKI,cAAc;YACnB,OAAOJ;QACT;QACA,MAAM,IAAIK,6BAAqB,CAAC;IAClC;IAfA,YACE,AAAiBJ,UAAsB,EACvC,AAAiBH,MAAkB,CACnC;QACA,KAAK,CAAC;YAAEQ,eAAe;YAASC,eAAe;YAAYC,mBAAmB;QAAK,SAHlEP,aAAAA,iBACAH,SAAAA;IAGnB;AAWF"}
1
+ {"version":3,"sources":["../../../../backend/src/authentication/guards/auth-local.strategy.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 { Injectable, UnauthorizedException } from '@nestjs/common'\nimport { AbstractStrategy, PassportStrategy } from '@nestjs/passport'\nimport type { FastifyRequest } from 'fastify'\nimport { PinoLogger } from 'nestjs-pino'\nimport { Strategy } from 'passport-local'\nimport type { UserModel } from '../../applications/users/models/user.model'\nimport { AuthMethod } from '../models/auth-method'\n\n@Injectable()\nexport class AuthLocalStrategy extends PassportStrategy(Strategy, 'local') implements AbstractStrategy {\n constructor(\n private readonly authMethod: AuthMethod,\n private readonly logger: PinoLogger\n ) {\n super({ usernameField: 'login', passwordField: 'password', passReqToCallback: true })\n }\n\n async validate(req: FastifyRequest, loginOrEmail: string, password: string): Promise<UserModel> {\n loginOrEmail = loginOrEmail.trim()\n password = password.trim()\n this.logger.assign({ user: loginOrEmail })\n const user: UserModel = await this.authMethod.validateUser(loginOrEmail, password, req.ip)\n if (user) {\n user.removePassword()\n return user\n }\n throw new UnauthorizedException('Wrong login or password')\n }\n}\n"],"names":["AuthLocalStrategy","PassportStrategy","Strategy","validate","req","loginOrEmail","password","trim","logger","assign","user","authMethod","validateUser","ip","removePassword","UnauthorizedException","usernameField","passwordField","passReqToCallback"],"mappings":"AAAA;;;;CAIC;;;;+BAWYA;;;eAAAA;;;wBATqC;0BACC;4BAExB;+BACF;4BAEE;;;;;;;;;;AAGpB,IAAA,AAAMA,oBAAN,MAAMA,0BAA0BC,IAAAA,0BAAgB,EAACC,uBAAQ,EAAE;IAQhE,MAAMC,SAASC,GAAmB,EAAEC,YAAoB,EAAEC,QAAgB,EAAsB;QAC9FD,eAAeA,aAAaE,IAAI;QAChCD,WAAWA,SAASC,IAAI;QACxB,IAAI,CAACC,MAAM,CAACC,MAAM,CAAC;YAAEC,MAAML;QAAa;QACxC,MAAMK,OAAkB,MAAM,IAAI,CAACC,UAAU,CAACC,YAAY,CAACP,cAAcC,UAAUF,IAAIS,EAAE;QACzF,IAAIH,MAAM;YACRA,KAAKI,cAAc;YACnB,OAAOJ;QACT;QACA,MAAM,IAAIK,6BAAqB,CAAC;IAClC;IAjBA,YACE,AAAiBJ,UAAsB,EACvC,AAAiBH,MAAkB,CACnC;QACA,KAAK,CAAC;YAAEQ,eAAe;YAASC,eAAe;YAAYC,mBAAmB;QAAK,SAHlEP,aAAAA,iBACAH,SAAAA;IAGnB;AAaF"}
@@ -32,7 +32,7 @@ function _ts_metadata(k, v) {
32
32
  }
33
33
  let AuthMethodLdapService = class AuthMethodLdapService {
34
34
  async validateUser(login, password, ip, scope) {
35
- let user = await this.usersManager.findUser(this.normalizeLogin(login), false);
35
+ let user = await this.usersManager.findUser(this.dbLogin(login), false);
36
36
  if (user) {
37
37
  if (user.isGuest) {
38
38
  // allow guests to be authenticated from db and check if the current user is defined as active
@@ -59,9 +59,9 @@ let AuthMethodLdapService = class AuthMethodLdapService {
59
59
  }
60
60
  }
61
61
  return null;
62
- } else if (!entry[this.loginAttribute] || !entry[this.emailAttribute]) {
62
+ } else if (!entry[this.ldapConfig.attributes.login] || !entry[this.ldapConfig.attributes.email]) {
63
63
  this.logger.error(`${this.validateUser.name} - required ldap fields are missing :
64
- [${this.loginAttribute}, ${this.emailAttribute}] =>
64
+ [${this.ldapConfig.attributes.login}, ${this.ldapConfig.attributes.email}] =>
65
65
  (${JSON.stringify(entry)})`);
66
66
  return null;
67
67
  }
@@ -71,30 +71,30 @@ let AuthMethodLdapService = class AuthMethodLdapService {
71
71
  return user;
72
72
  }
73
73
  async checkAuth(login, password) {
74
- const loginAttr = this.loginAttribute;
75
- const isAD = loginAttr === _authldap.LDAP_LOGIN_ATTR.SAM || loginAttr === _authldap.LDAP_LOGIN_ATTR.UPN;
74
+ const ldapLogin = this.buildLdapLogin(login);
75
+ const isAD = this.ldapConfig.attributes.login === _authldap.LDAP_LOGIN_ATTR.SAM || this.ldapConfig.attributes.login === _authldap.LDAP_LOGIN_ATTR.UPN;
76
76
  // AD: bind directly with the user input (UPN or DOMAIN\user)
77
77
  // Generic LDAP: build DN from login attribute + baseDN
78
- const bindUserDN = isAD ? login : `${loginAttr}=${login},${this.baseDN}`;
78
+ const bindUserDN = isAD ? ldapLogin : `${this.ldapConfig.attributes.login}=${ldapLogin},${this.ldapConfig.baseDN}`;
79
79
  let client;
80
80
  let error;
81
- for (const s of this.servers){
81
+ for (const s of this.ldapConfig.servers){
82
82
  client = new _ldapts.Client({
83
83
  ...this.clientOptions,
84
84
  url: s
85
85
  });
86
86
  try {
87
87
  await client.bind(bindUserDN, password);
88
- return await this.checkAccess(client, login);
88
+ return await this.checkAccess(ldapLogin, client);
89
89
  } catch (e) {
90
90
  if (e.errors?.length) {
91
91
  for (const err of e.errors){
92
- this.logger.warn(`${this.checkAuth.name} - ${login} : ${err}`);
92
+ this.logger.warn(`${this.checkAuth.name} - ${ldapLogin} : ${err}`);
93
93
  error = err;
94
94
  }
95
95
  } else {
96
96
  error = e;
97
- this.logger.warn(`${this.checkAuth.name} - ${login} : ${e}`);
97
+ this.logger.warn(`${this.checkAuth.name} - ${ldapLogin} : ${e}`);
98
98
  }
99
99
  if (error instanceof _ldapts.InvalidCredentialsError) {
100
100
  return false;
@@ -108,10 +108,10 @@ let AuthMethodLdapService = class AuthMethodLdapService {
108
108
  }
109
109
  return false;
110
110
  }
111
- async checkAccess(client, login) {
112
- const searchFilter = this.buildUserFilter(login, this.filter);
111
+ async checkAccess(login, client) {
112
+ const searchFilter = this.buildUserFilter(login, this.ldapConfig.filter);
113
113
  try {
114
- const { searchEntries } = await client.search(this.baseDN, {
114
+ const { searchEntries } = await client.search(this.ldapConfig.baseDN, {
115
115
  scope: 'sub',
116
116
  filter: searchFilter,
117
117
  attributes: _authldap.ALL_LDAP_ATTRIBUTES
@@ -134,7 +134,8 @@ let AuthMethodLdapService = class AuthMethodLdapService {
134
134
  }
135
135
  async updateOrCreateUser(identity, user) {
136
136
  if (user === null) {
137
- const createdUser = await this.adminUsersManager.createUserOrGuest(identity, _user.USER_ROLE.USER);
137
+ // create
138
+ const createdUser = await this.adminUsersManager.createUserOrGuest(identity, identity.role);
138
139
  const freshUser = await this.usersManager.fromUserId(createdUser.id);
139
140
  if (!freshUser) {
140
141
  this.logger.error(`${this.updateOrCreateUser.name} - user was not found : ${createdUser.login} (${createdUser.id})`);
@@ -146,7 +147,7 @@ let AuthMethodLdapService = class AuthMethodLdapService {
146
147
  this.logger.error(`${this.updateOrCreateUser.name} - user login mismatch : ${identity.login} !== ${user.login}`);
147
148
  throw new _common.HttpException('Account matching error', _common.HttpStatus.FORBIDDEN);
148
149
  }
149
- // check if user information has changed
150
+ // update: check if user information has changed
150
151
  const identityHasChanged = Object.fromEntries((await Promise.all(Object.keys(identity).map(async (key)=>{
151
152
  if (key === 'password') {
152
153
  const isSame = await (0, _functions.comparePassword)(identity[key], user.password);
@@ -162,7 +163,15 @@ let AuthMethodLdapService = class AuthMethodLdapService {
162
163
  }))).filter(Boolean));
163
164
  if (Object.keys(identityHasChanged).length > 0) {
164
165
  try {
166
+ if (identityHasChanged?.role != null) {
167
+ if (user.role === _user.USER_ROLE.ADMINISTRATOR && !this.ldapConfig.adminGroup) {
168
+ // Prevent removing admin role when adminGroup was removed or not defined
169
+ delete identityHasChanged.role;
170
+ }
171
+ }
172
+ // Update user properties
165
173
  await this.adminUsersManager.updateUserOrGuest(user.id, identityHasChanged);
174
+ // Extra stuff
166
175
  if (identityHasChanged?.password) {
167
176
  delete identityHasChanged.password;
168
177
  }
@@ -179,17 +188,26 @@ let AuthMethodLdapService = class AuthMethodLdapService {
179
188
  }
180
189
  convertToLdapUserEntry(entry) {
181
190
  for (const attr of _authldap.ALL_LDAP_ATTRIBUTES){
191
+ if (attr === _authldap.LDAP_COMMON_ATTR.MEMBER_OF && entry[attr]) {
192
+ entry[attr] = (Array.isArray(entry[attr]) ? entry[attr] : entry[attr] ? [
193
+ entry[attr]
194
+ ] : []).filter((v)=>typeof v === 'string').map((v)=>v.match(/cn\s*=\s*([^,]+)/i)?.[1]?.trim()).filter(Boolean);
195
+ continue;
196
+ }
182
197
  if (Array.isArray(entry[attr])) {
198
+ // Keep only the first value for all other attributes (e.g., email)
183
199
  entry[attr] = entry[attr].length > 0 ? entry[attr][0] : null;
184
200
  }
185
201
  }
186
202
  return entry;
187
203
  }
188
204
  createIdentity(entry, password) {
205
+ const isAdmin = typeof this.ldapConfig.adminGroup === 'string' && this.ldapConfig.adminGroup && entry[_authldap.LDAP_COMMON_ATTR.MEMBER_OF]?.includes(this.ldapConfig.adminGroup);
189
206
  return {
190
- login: this.normalizeLogin(entry[this.loginAttribute]),
191
- email: entry[this.emailAttribute],
207
+ login: this.dbLogin(entry[this.ldapConfig.attributes.login]),
208
+ email: entry[this.ldapConfig.attributes.email],
192
209
  password: password,
210
+ role: isAdmin ? _user.USER_ROLE.ADMINISTRATOR : _user.USER_ROLE.USER,
193
211
  ...this.getFirstNameAndLastName(entry)
194
212
  };
195
213
  }
@@ -215,25 +233,54 @@ let AuthMethodLdapService = class AuthMethodLdapService {
215
233
  lastName: ''
216
234
  };
217
235
  }
218
- normalizeLogin(login, toLowerCase = true) {
219
- const normalized = (login.includes('\\') ? login.split('\\').slice(-1)[0] : login).trim();
220
- return toLowerCase ? normalized.toLowerCase() : normalized;
236
+ dbLogin(login) {
237
+ if (login.includes('@')) {
238
+ return login.split('@')[0];
239
+ } else if (login.includes('\\')) {
240
+ return login.split('\\').slice(-1)[0];
241
+ }
242
+ return login;
243
+ }
244
+ buildLdapLogin(login) {
245
+ if (this.ldapConfig.attributes.login === _authldap.LDAP_LOGIN_ATTR.UPN) {
246
+ if (this.ldapConfig.upnSuffix && !login.includes('@')) {
247
+ return `${login}@${this.ldapConfig.upnSuffix}`;
248
+ }
249
+ } else if (this.ldapConfig.attributes.login === _authldap.LDAP_LOGIN_ATTR.SAM) {
250
+ if (this.ldapConfig.netbiosName && !login.includes('\\')) {
251
+ return `${this.ldapConfig.netbiosName}\\${login}`;
252
+ }
253
+ }
254
+ return login;
221
255
  }
222
256
  buildUserFilter(login, extraFilter) {
223
257
  // Build a safe LDAP filter to search for a user.
224
258
  // Important: - Values passed to EqualityFilter are auto-escaped by ldapts
225
259
  // - extraFilter is appended as-is (assumed trusted configuration)
226
- // Output: (&(|(userPrincipalName=john.doe)(sAMAccountName=john.doe)(uid=john.doe))(*extraFilter*))
227
- // OR clause: accept UPN, sAMAccountName, or uid
228
- const normalizedLogin = this.normalizeLogin(login, false);
229
- const eq = new _ldapts.EqualityFilter({
230
- attribute: this.loginAttribute,
231
- value: normalizedLogin
260
+ // Output: (&(|(userPrincipalName=john.doe@sync-in.com)(sAMAccountName=john.doe)(uid=john.doe))(*extraFilter*))
261
+ // Handle the case where the sAMAccountName is provided in domain-qualified format (e.g., SYNC_IN\\user)
262
+ // Note: sAMAccountName is always stored without the domain in Active Directory.
263
+ const uid = this.dbLogin(login);
264
+ const or = new _ldapts.OrFilter({
265
+ filters: [
266
+ new _ldapts.EqualityFilter({
267
+ attribute: _authldap.LDAP_LOGIN_ATTR.UPN,
268
+ value: login
269
+ }),
270
+ new _ldapts.EqualityFilter({
271
+ attribute: _authldap.LDAP_LOGIN_ATTR.SAM,
272
+ value: uid
273
+ }),
274
+ new _ldapts.EqualityFilter({
275
+ attribute: _authldap.LDAP_LOGIN_ATTR.UID,
276
+ value: uid
277
+ })
278
+ ]
232
279
  });
233
280
  // Convert to LDAP filter string
234
281
  let filterString = new _ldapts.AndFilter({
235
282
  filters: [
236
- eq
283
+ or
237
284
  ]
238
285
  }).toString();
239
286
  // Optionally append an extra filter from config (trusted source)
@@ -246,11 +293,7 @@ let AuthMethodLdapService = class AuthMethodLdapService {
246
293
  this.usersManager = usersManager;
247
294
  this.adminUsersManager = adminUsersManager;
248
295
  this.logger = new _common.Logger(AuthMethodLdapService.name);
249
- this.loginAttribute = _configenvironment.configuration.auth.ldap.attributes.login;
250
- this.emailAttribute = _configenvironment.configuration.auth.ldap.attributes.email;
251
- this.servers = _configenvironment.configuration.auth.ldap.servers;
252
- this.baseDN = _configenvironment.configuration.auth.ldap.baseDN;
253
- this.filter = _configenvironment.configuration.auth.ldap.filter;
296
+ this.ldapConfig = _configenvironment.configuration.auth.ldap;
254
297
  this.clientOptions = {
255
298
  timeout: 6000,
256
299
  connectTimeout: 6000,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../backend/src/authentication/services/auth-methods/auth-method-ldap.service.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 { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'\nimport { AndFilter, Client, ClientOptions, Entry, EqualityFilter, InvalidCredentialsError } from 'ldapts'\nimport { CONNECT_ERROR_CODE } from '../../../app.constants'\nimport { USER_ROLE } from '../../../applications/users/constants/user'\nimport type { CreateUserDto, UpdateUserDto } from '../../../applications/users/dto/create-or-update-user.dto'\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 { comparePassword, splitFullName } from '../../../common/functions'\nimport { configuration } from '../../../configuration/config.environment'\nimport { ALL_LDAP_ATTRIBUTES, LDAP_COMMON_ATTR, LDAP_LOGIN_ATTR } from '../../constants/auth-ldap'\nimport type { AUTH_SCOPE } from '../../constants/scope'\nimport { AuthMethod } from '../../models/auth-method'\n\ntype LdapUserEntry = Entry & Record<LDAP_LOGIN_ATTR | (typeof LDAP_COMMON_ATTR)[keyof typeof LDAP_COMMON_ATTR], string>\n\n@Injectable()\nexport class AuthMethodLdapService implements AuthMethod {\n private readonly logger = new Logger(AuthMethodLdapService.name)\n private readonly loginAttribute: LDAP_LOGIN_ATTR = configuration.auth.ldap.attributes.login\n private readonly emailAttribute: string = configuration.auth.ldap.attributes.email\n private readonly servers: string[] = configuration.auth.ldap.servers\n private readonly baseDN: string = configuration.auth.ldap.baseDN\n private readonly filter: string = configuration.auth.ldap.filter\n private clientOptions: ClientOptions = { timeout: 6000, connectTimeout: 6000, url: '' }\n\n constructor(\n private readonly usersManager: UsersManager,\n private readonly adminUsersManager: AdminUsersManager\n ) {}\n\n async validateUser(login: string, password: string, ip?: string, scope?: AUTH_SCOPE): Promise<UserModel> {\n let user: UserModel = await this.usersManager.findUser(this.normalizeLogin(login), false)\n if (user) {\n if (user.isGuest) {\n // allow guests to be authenticated from db and check if the current user is defined as active\n return this.usersManager.logUser(user, password, ip)\n }\n if (!user.isActive) {\n this.logger.error(`${this.validateUser.name} - user *${user.login}* is locked`)\n throw new HttpException('Account locked', HttpStatus.FORBIDDEN)\n }\n }\n const entry: false | LdapUserEntry = await this.checkAuth(login, password)\n if (entry === false) {\n // LDAP auth failed\n if (user) {\n let authSuccess = false\n if (scope) {\n // try user app password\n authSuccess = await this.usersManager.validateAppPassword(user, password, ip, scope)\n }\n this.usersManager.updateAccesses(user, ip, authSuccess).catch((e: Error) => this.logger.error(`${this.validateUser.name} : ${e}`))\n if (authSuccess) {\n // logged with app password\n return user\n }\n }\n return null\n } else if (!entry[this.loginAttribute] || !entry[this.emailAttribute]) {\n this.logger.error(`${this.validateUser.name} - required ldap fields are missing : \n [${this.loginAttribute}, ${this.emailAttribute}] => \n (${JSON.stringify(entry)})`)\n return null\n }\n const identity = this.createIdentity(entry, password)\n user = await this.updateOrCreateUser(identity, user)\n this.usersManager.updateAccesses(user, ip, true).catch((e: Error) => this.logger.error(`${this.validateUser.name} : ${e}`))\n return user\n }\n\n private async checkAuth(login: string, password: string): Promise<LdapUserEntry | false> {\n const loginAttr = this.loginAttribute\n const isAD = loginAttr === LDAP_LOGIN_ATTR.SAM || loginAttr === LDAP_LOGIN_ATTR.UPN\n // AD: bind directly with the user input (UPN or DOMAIN\\user)\n // Generic LDAP: build DN from login attribute + baseDN\n const bindUserDN = isAD ? login : `${loginAttr}=${login},${this.baseDN}`\n let client: Client\n let error: any\n for (const s of this.servers) {\n client = new Client({ ...this.clientOptions, url: s })\n try {\n await client.bind(bindUserDN, password)\n return await this.checkAccess(client, login)\n } catch (e) {\n if (e.errors?.length) {\n for (const err of e.errors) {\n this.logger.warn(`${this.checkAuth.name} - ${login} : ${err}`)\n error = err\n }\n } else {\n error = e\n this.logger.warn(`${this.checkAuth.name} - ${login} : ${e}`)\n }\n if (error instanceof InvalidCredentialsError) {\n return false\n }\n } finally {\n await client.unbind()\n }\n }\n if (error && CONNECT_ERROR_CODE.has(error.code)) {\n throw new HttpException('Authentication service error', HttpStatus.INTERNAL_SERVER_ERROR)\n }\n return false\n }\n\n private async checkAccess(client: Client, login: string): Promise<LdapUserEntry | false> {\n const searchFilter = this.buildUserFilter(login, this.filter)\n try {\n const { searchEntries } = await client.search(this.baseDN, {\n scope: 'sub',\n filter: searchFilter,\n attributes: ALL_LDAP_ATTRIBUTES\n })\n\n if (searchEntries.length === 0) {\n this.logger.debug(`${this.checkAccess.name} - search filter : ${searchFilter}`)\n this.logger.warn(`${this.checkAccess.name} - no LDAP entry found for : ${login}`)\n return false\n }\n\n if (searchEntries.length > 1) {\n this.logger.warn(`${this.checkAccess.name} - multiple LDAP entries found for : ${login}, using first one`)\n }\n\n // Always return the first valid entry\n return this.convertToLdapUserEntry(searchEntries[0])\n } catch (e) {\n this.logger.debug(`${this.checkAccess.name} - search filter : ${searchFilter}`)\n this.logger.error(`${this.checkAccess.name} - ${login} : ${e}`)\n return false\n }\n }\n\n private async updateOrCreateUser(identity: CreateUserDto, user: UserModel): Promise<UserModel> {\n if (user === null) {\n const createdUser = await this.adminUsersManager.createUserOrGuest(identity, USER_ROLE.USER)\n const freshUser = await this.usersManager.fromUserId(createdUser.id)\n if (!freshUser) {\n this.logger.error(`${this.updateOrCreateUser.name} - user was not found : ${createdUser.login} (${createdUser.id})`)\n throw new HttpException('User not found', HttpStatus.NOT_FOUND)\n }\n return freshUser\n }\n if (identity.login !== user.login) {\n this.logger.error(`${this.updateOrCreateUser.name} - user login mismatch : ${identity.login} !== ${user.login}`)\n throw new HttpException('Account matching error', HttpStatus.FORBIDDEN)\n }\n // check if user information has changed\n const identityHasChanged: UpdateUserDto = Object.fromEntries(\n (\n await Promise.all(\n Object.keys(identity).map(async (key: string) => {\n if (key === 'password') {\n const isSame = await comparePassword(identity[key], user.password)\n return isSame ? null : [key, identity[key]]\n }\n return identity[key] !== user[key] ? [key, identity[key]] : null\n })\n )\n ).filter(Boolean)\n )\n if (Object.keys(identityHasChanged).length > 0) {\n try {\n await this.adminUsersManager.updateUserOrGuest(user.id, identityHasChanged)\n if (identityHasChanged?.password) {\n delete identityHasChanged.password\n }\n Object.assign(user, identityHasChanged)\n if ('lastName' in identityHasChanged || 'firstName' in identityHasChanged) {\n // force fullName update in current user model\n user.setFullName(true)\n }\n } catch (e) {\n this.logger.warn(`${this.updateOrCreateUser.name} - unable to update user *${user.login}* : ${e}`)\n }\n }\n return user\n }\n\n private convertToLdapUserEntry(entry: Entry): LdapUserEntry {\n for (const attr of ALL_LDAP_ATTRIBUTES) {\n if (Array.isArray(entry[attr])) {\n entry[attr] = entry[attr].length > 0 ? entry[attr][0] : null\n }\n }\n return entry as LdapUserEntry\n }\n\n private createIdentity(entry: LdapUserEntry, password: string): CreateUserDto {\n return {\n login: this.normalizeLogin(entry[this.loginAttribute]),\n email: entry[this.emailAttribute] as string,\n password: password,\n ...this.getFirstNameAndLastName(entry)\n } satisfies CreateUserDto\n }\n\n private getFirstNameAndLastName(entry: LdapUserEntry): { firstName: string; lastName: string } {\n // 1) Prefer structured attributes\n if (entry.sn && entry.givenName) {\n return { firstName: entry.givenName, lastName: entry.sn }\n }\n // 2) Fallback to displayName if available\n if (entry.displayName && entry.displayName.trim()) {\n return splitFullName(entry.displayName)\n }\n // 3) Fallback to cn\n if (entry.cn && entry.cn.trim()) {\n return splitFullName(entry.cn)\n }\n // 4) Nothing usable\n return { firstName: '', lastName: '' }\n }\n\n private normalizeLogin(login: string, toLowerCase = true): string {\n const normalized = (login.includes('\\\\') ? login.split('\\\\').slice(-1)[0] : login).trim()\n return toLowerCase ? normalized.toLowerCase() : normalized\n }\n\n private buildUserFilter(login: string, extraFilter?: string): string {\n // Build a safe LDAP filter to search for a user.\n // Important: - Values passed to EqualityFilter are auto-escaped by ldapts\n // - extraFilter is appended as-is (assumed trusted configuration)\n // Output: (&(|(userPrincipalName=john.doe)(sAMAccountName=john.doe)(uid=john.doe))(*extraFilter*))\n\n // OR clause: accept UPN, sAMAccountName, or uid\n const normalizedLogin = this.normalizeLogin(login, false)\n\n const eq = new EqualityFilter({ attribute: this.loginAttribute, value: normalizedLogin })\n // Convert to LDAP filter string\n let filterString = new AndFilter({ filters: [eq] }).toString()\n\n // Optionally append an extra filter from config (trusted source)\n if (extraFilter && extraFilter.trim()) {\n filterString = `(&${filterString}${extraFilter})`\n }\n return filterString\n }\n}\n"],"names":["AuthMethodLdapService","validateUser","login","password","ip","scope","user","usersManager","findUser","normalizeLogin","isGuest","logUser","isActive","logger","error","name","HttpException","HttpStatus","FORBIDDEN","entry","checkAuth","authSuccess","validateAppPassword","updateAccesses","catch","e","loginAttribute","emailAttribute","JSON","stringify","identity","createIdentity","updateOrCreateUser","loginAttr","isAD","LDAP_LOGIN_ATTR","SAM","UPN","bindUserDN","baseDN","client","s","servers","Client","clientOptions","url","bind","checkAccess","errors","length","err","warn","InvalidCredentialsError","unbind","CONNECT_ERROR_CODE","has","code","INTERNAL_SERVER_ERROR","searchFilter","buildUserFilter","filter","searchEntries","search","attributes","ALL_LDAP_ATTRIBUTES","debug","convertToLdapUserEntry","createdUser","adminUsersManager","createUserOrGuest","USER_ROLE","USER","freshUser","fromUserId","id","NOT_FOUND","identityHasChanged","Object","fromEntries","Promise","all","keys","map","key","isSame","comparePassword","Boolean","updateUserOrGuest","assign","setFullName","attr","Array","isArray","email","getFirstNameAndLastName","sn","givenName","firstName","lastName","displayName","trim","splitFullName","cn","toLowerCase","normalized","includes","split","slice","extraFilter","normalizedLogin","eq","EqualityFilter","attribute","value","filterString","AndFilter","filters","toString","Logger","configuration","auth","ldap","timeout","connectTimeout"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../backend/src/authentication/services/auth-methods/auth-method-ldap.service.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 { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'\nimport { AndFilter, Client, ClientOptions, Entry, EqualityFilter, InvalidCredentialsError, OrFilter } from 'ldapts'\nimport { CONNECT_ERROR_CODE } from '../../../app.constants'\nimport { USER_ROLE } from '../../../applications/users/constants/user'\nimport type { CreateUserDto, UpdateUserDto } from '../../../applications/users/dto/create-or-update-user.dto'\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 { comparePassword, splitFullName } from '../../../common/functions'\nimport { configuration } from '../../../configuration/config.environment'\nimport { ALL_LDAP_ATTRIBUTES, LDAP_COMMON_ATTR, LDAP_LOGIN_ATTR } from '../../constants/auth-ldap'\nimport type { AUTH_SCOPE } from '../../constants/scope'\nimport { AuthMethod } from '../../models/auth-method'\n\ntype LdapUserEntry = Entry & Record<LDAP_LOGIN_ATTR | (typeof LDAP_COMMON_ATTR)[keyof typeof LDAP_COMMON_ATTR], string>\n\n@Injectable()\nexport class AuthMethodLdapService implements AuthMethod {\n private readonly logger = new Logger(AuthMethodLdapService.name)\n private readonly ldapConfig = configuration.auth.ldap\n private clientOptions: ClientOptions = { timeout: 6000, connectTimeout: 6000, url: '' }\n\n constructor(\n private readonly usersManager: UsersManager,\n private readonly adminUsersManager: AdminUsersManager\n ) {}\n\n async validateUser(login: string, password: string, ip?: string, scope?: AUTH_SCOPE): Promise<UserModel> {\n let user: UserModel = await this.usersManager.findUser(this.dbLogin(login), false)\n if (user) {\n if (user.isGuest) {\n // allow guests to be authenticated from db and check if the current user is defined as active\n return this.usersManager.logUser(user, password, ip)\n }\n if (!user.isActive) {\n this.logger.error(`${this.validateUser.name} - user *${user.login}* is locked`)\n throw new HttpException('Account locked', HttpStatus.FORBIDDEN)\n }\n }\n const entry: false | LdapUserEntry = await this.checkAuth(login, password)\n if (entry === false) {\n // LDAP auth failed\n if (user) {\n let authSuccess = false\n if (scope) {\n // try user app password\n authSuccess = await this.usersManager.validateAppPassword(user, password, ip, scope)\n }\n this.usersManager.updateAccesses(user, ip, authSuccess).catch((e: Error) => this.logger.error(`${this.validateUser.name} : ${e}`))\n if (authSuccess) {\n // logged with app password\n return user\n }\n }\n return null\n } else if (!entry[this.ldapConfig.attributes.login] || !entry[this.ldapConfig.attributes.email]) {\n this.logger.error(`${this.validateUser.name} - required ldap fields are missing : \n [${this.ldapConfig.attributes.login}, ${this.ldapConfig.attributes.email}] => \n (${JSON.stringify(entry)})`)\n return null\n }\n const identity = this.createIdentity(entry, password)\n user = await this.updateOrCreateUser(identity, user)\n this.usersManager.updateAccesses(user, ip, true).catch((e: Error) => this.logger.error(`${this.validateUser.name} : ${e}`))\n return user\n }\n\n private async checkAuth(login: string, password: string): Promise<LdapUserEntry | false> {\n const ldapLogin = this.buildLdapLogin(login)\n const isAD = this.ldapConfig.attributes.login === LDAP_LOGIN_ATTR.SAM || this.ldapConfig.attributes.login === LDAP_LOGIN_ATTR.UPN\n // AD: bind directly with the user input (UPN or DOMAIN\\user)\n // Generic LDAP: build DN from login attribute + baseDN\n const bindUserDN = isAD ? ldapLogin : `${this.ldapConfig.attributes.login}=${ldapLogin},${this.ldapConfig.baseDN}`\n let client: Client\n let error: any\n for (const s of this.ldapConfig.servers) {\n client = new Client({ ...this.clientOptions, url: s })\n try {\n await client.bind(bindUserDN, password)\n return await this.checkAccess(ldapLogin, client)\n } catch (e) {\n if (e.errors?.length) {\n for (const err of e.errors) {\n this.logger.warn(`${this.checkAuth.name} - ${ldapLogin} : ${err}`)\n error = err\n }\n } else {\n error = e\n this.logger.warn(`${this.checkAuth.name} - ${ldapLogin} : ${e}`)\n }\n if (error instanceof InvalidCredentialsError) {\n return false\n }\n } finally {\n await client.unbind()\n }\n }\n if (error && CONNECT_ERROR_CODE.has(error.code)) {\n throw new HttpException('Authentication service error', HttpStatus.INTERNAL_SERVER_ERROR)\n }\n return false\n }\n\n private async checkAccess(login: string, client: Client): Promise<LdapUserEntry | false> {\n const searchFilter = this.buildUserFilter(login, this.ldapConfig.filter)\n try {\n const { searchEntries } = await client.search(this.ldapConfig.baseDN, {\n scope: 'sub',\n filter: searchFilter,\n attributes: ALL_LDAP_ATTRIBUTES\n })\n\n if (searchEntries.length === 0) {\n this.logger.debug(`${this.checkAccess.name} - search filter : ${searchFilter}`)\n this.logger.warn(`${this.checkAccess.name} - no LDAP entry found for : ${login}`)\n return false\n }\n\n if (searchEntries.length > 1) {\n this.logger.warn(`${this.checkAccess.name} - multiple LDAP entries found for : ${login}, using first one`)\n }\n\n // Always return the first valid entry\n return this.convertToLdapUserEntry(searchEntries[0])\n } catch (e) {\n this.logger.debug(`${this.checkAccess.name} - search filter : ${searchFilter}`)\n this.logger.error(`${this.checkAccess.name} - ${login} : ${e}`)\n return false\n }\n }\n\n private async updateOrCreateUser(identity: CreateUserDto, user: UserModel): Promise<UserModel> {\n if (user === null) {\n // create\n const createdUser = await this.adminUsersManager.createUserOrGuest(identity, identity.role)\n const freshUser = await this.usersManager.fromUserId(createdUser.id)\n if (!freshUser) {\n this.logger.error(`${this.updateOrCreateUser.name} - user was not found : ${createdUser.login} (${createdUser.id})`)\n throw new HttpException('User not found', HttpStatus.NOT_FOUND)\n }\n return freshUser\n }\n if (identity.login !== user.login) {\n this.logger.error(`${this.updateOrCreateUser.name} - user login mismatch : ${identity.login} !== ${user.login}`)\n throw new HttpException('Account matching error', HttpStatus.FORBIDDEN)\n }\n // update: check if user information has changed\n const identityHasChanged: UpdateUserDto = Object.fromEntries(\n (\n await Promise.all(\n Object.keys(identity).map(async (key: string) => {\n if (key === 'password') {\n const isSame = await comparePassword(identity[key], user.password)\n return isSame ? null : [key, identity[key]]\n }\n return identity[key] !== user[key] ? [key, identity[key]] : null\n })\n )\n ).filter(Boolean)\n )\n if (Object.keys(identityHasChanged).length > 0) {\n try {\n if (identityHasChanged?.role != null) {\n if (user.role === USER_ROLE.ADMINISTRATOR && !this.ldapConfig.adminGroup) {\n // Prevent removing admin role when adminGroup was removed or not defined\n delete identityHasChanged.role\n }\n }\n // Update user properties\n await this.adminUsersManager.updateUserOrGuest(user.id, identityHasChanged)\n // Extra stuff\n if (identityHasChanged?.password) {\n delete identityHasChanged.password\n }\n Object.assign(user, identityHasChanged)\n if ('lastName' in identityHasChanged || 'firstName' in identityHasChanged) {\n // force fullName update in current user model\n user.setFullName(true)\n }\n } catch (e) {\n this.logger.warn(`${this.updateOrCreateUser.name} - unable to update user *${user.login}* : ${e}`)\n }\n }\n return user\n }\n\n private convertToLdapUserEntry(entry: Entry): LdapUserEntry {\n for (const attr of ALL_LDAP_ATTRIBUTES) {\n if (attr === LDAP_COMMON_ATTR.MEMBER_OF && entry[attr]) {\n entry[attr] = (Array.isArray(entry[attr]) ? entry[attr] : entry[attr] ? [entry[attr]] : [])\n .filter((v: any) => typeof v === 'string')\n .map((v) => v.match(/cn\\s*=\\s*([^,]+)/i)?.[1]?.trim())\n .filter(Boolean)\n continue\n }\n if (Array.isArray(entry[attr])) {\n // Keep only the first value for all other attributes (e.g., email)\n entry[attr] = entry[attr].length > 0 ? entry[attr][0] : null\n }\n }\n return entry as LdapUserEntry\n }\n\n private createIdentity(entry: LdapUserEntry, password: string): CreateUserDto {\n const isAdmin =\n typeof this.ldapConfig.adminGroup === 'string' &&\n this.ldapConfig.adminGroup &&\n entry[LDAP_COMMON_ATTR.MEMBER_OF]?.includes(this.ldapConfig.adminGroup)\n return {\n login: this.dbLogin(entry[this.ldapConfig.attributes.login]),\n email: entry[this.ldapConfig.attributes.email] as string,\n password: password,\n role: isAdmin ? USER_ROLE.ADMINISTRATOR : USER_ROLE.USER,\n ...this.getFirstNameAndLastName(entry)\n } satisfies CreateUserDto\n }\n\n private getFirstNameAndLastName(entry: LdapUserEntry): { firstName: string; lastName: string } {\n // 1) Prefer structured attributes\n if (entry.sn && entry.givenName) {\n return { firstName: entry.givenName, lastName: entry.sn }\n }\n // 2) Fallback to displayName if available\n if (entry.displayName && entry.displayName.trim()) {\n return splitFullName(entry.displayName)\n }\n // 3) Fallback to cn\n if (entry.cn && entry.cn.trim()) {\n return splitFullName(entry.cn)\n }\n // 4) Nothing usable\n return { firstName: '', lastName: '' }\n }\n\n private dbLogin(login: string): string {\n if (login.includes('@')) {\n return login.split('@')[0]\n } else if (login.includes('\\\\')) {\n return login.split('\\\\').slice(-1)[0]\n }\n return login\n }\n\n private buildLdapLogin(login: string): string {\n if (this.ldapConfig.attributes.login === LDAP_LOGIN_ATTR.UPN) {\n if (this.ldapConfig.upnSuffix && !login.includes('@')) {\n return `${login}@${this.ldapConfig.upnSuffix}`\n }\n } else if (this.ldapConfig.attributes.login === LDAP_LOGIN_ATTR.SAM) {\n if (this.ldapConfig.netbiosName && !login.includes('\\\\')) {\n return `${this.ldapConfig.netbiosName}\\\\${login}`\n }\n }\n return login\n }\n\n private buildUserFilter(login: string, extraFilter?: string): string {\n // Build a safe LDAP filter to search for a user.\n // Important: - Values passed to EqualityFilter are auto-escaped by ldapts\n // - extraFilter is appended as-is (assumed trusted configuration)\n // Output: (&(|(userPrincipalName=john.doe@sync-in.com)(sAMAccountName=john.doe)(uid=john.doe))(*extraFilter*))\n\n // Handle the case where the sAMAccountName is provided in domain-qualified format (e.g., SYNC_IN\\\\user)\n // Note: sAMAccountName is always stored without the domain in Active Directory.\n const uid = this.dbLogin(login)\n\n const or = new OrFilter({\n filters: [\n new EqualityFilter({ attribute: LDAP_LOGIN_ATTR.UPN, value: login }),\n new EqualityFilter({ attribute: LDAP_LOGIN_ATTR.SAM, value: uid }),\n new EqualityFilter({ attribute: LDAP_LOGIN_ATTR.UID, value: uid })\n ]\n })\n\n // Convert to LDAP filter string\n let filterString = new AndFilter({ filters: [or] }).toString()\n\n // Optionally append an extra filter from config (trusted source)\n if (extraFilter && extraFilter.trim()) {\n filterString = `(&${filterString}${extraFilter})`\n }\n return filterString\n }\n}\n"],"names":["AuthMethodLdapService","validateUser","login","password","ip","scope","user","usersManager","findUser","dbLogin","isGuest","logUser","isActive","logger","error","name","HttpException","HttpStatus","FORBIDDEN","entry","checkAuth","authSuccess","validateAppPassword","updateAccesses","catch","e","ldapConfig","attributes","email","JSON","stringify","identity","createIdentity","updateOrCreateUser","ldapLogin","buildLdapLogin","isAD","LDAP_LOGIN_ATTR","SAM","UPN","bindUserDN","baseDN","client","s","servers","Client","clientOptions","url","bind","checkAccess","errors","length","err","warn","InvalidCredentialsError","unbind","CONNECT_ERROR_CODE","has","code","INTERNAL_SERVER_ERROR","searchFilter","buildUserFilter","filter","searchEntries","search","ALL_LDAP_ATTRIBUTES","debug","convertToLdapUserEntry","createdUser","adminUsersManager","createUserOrGuest","role","freshUser","fromUserId","id","NOT_FOUND","identityHasChanged","Object","fromEntries","Promise","all","keys","map","key","isSame","comparePassword","Boolean","USER_ROLE","ADMINISTRATOR","adminGroup","updateUserOrGuest","assign","setFullName","attr","LDAP_COMMON_ATTR","MEMBER_OF","Array","isArray","v","match","trim","isAdmin","includes","USER","getFirstNameAndLastName","sn","givenName","firstName","lastName","displayName","splitFullName","cn","split","slice","upnSuffix","netbiosName","extraFilter","uid","or","OrFilter","filters","EqualityFilter","attribute","value","UID","filterString","AndFilter","toString","Logger","configuration","auth","ldap","timeout","connectTimeout"],"mappings":"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"}
@@ -279,7 +279,8 @@ describe(_authmethodldapservice.AuthMethodLdapService.name, ()=>{
279
279
  email: 'john@example.org',
280
280
  password: 'pwd',
281
281
  firstName: 'John',
282
- lastName: 'Doe'
282
+ lastName: 'Doe',
283
+ role: 1
283
284
  }, expect.anything() // USER_ROLE.USER
284
285
  );
285
286
  expect(resB).toBe(createdUser);