UNPKG

@sync-in/server

Version:

The secure, open-source platform for file storage, sharing, collaboration, and sync

185 lines (184 loc) 8.44 kB
/* * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com> * This file is part of Sync-in | The open source file sync and share solution * See the LICENSE file for licensing details */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "AuthMethodLdapService", { enumerable: true, get: function() { return AuthMethodLdapService; } }); const _common = require("@nestjs/common"); const _ldapts = require("ldapts"); const _appconstants = require("../../../app.constants"); const _user = require("../../../applications/users/constants/user"); const _adminusersmanagerservice = require("../../../applications/users/services/admin-users-manager.service"); const _usersmanagerservice = require("../../../applications/users/services/users-manager.service"); const _functions = require("../../../common/functions"); const _configenvironment = require("../../../configuration/config.environment"); function _ts_decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } function _ts_metadata(k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); } let AuthMethodLdapService = class AuthMethodLdapService { async validateUser(loginOrEmail, password, ip) { let user = await this.usersManager.findUser(loginOrEmail, false); if (user) { if (user.isGuest) { // allow guests to be authenticated from db & check if current user is defined as active return this.usersManager.logUser(user, password, ip); } if (!user.isActive) { this.logger.error(`${this.validateUser.name} - user *${user.login}* is locked`); throw new _common.HttpException('Account locked', _common.HttpStatus.FORBIDDEN); } } const entry = await this.checkAuth(loginOrEmail, password); if (entry === false) { if (user) { this.usersManager.updateAccesses(user, ip, false).catch((e)=>this.logger.error(`${this.validateUser.name} : ${e}`)); } return null; } else if (!entry.mail || !entry.uid) { this.logger.error(`${this.validateUser.name} - ${loginOrEmail} : some ldap fields are missing => (${JSON.stringify(entry)})`); return null; } const identity = { login: entry.uid, email: entry.mail, password: password, ...(0, _functions.splitFullName)(entry.cn) }; user = await this.updateOrCreateUser(identity, user); this.usersManager.updateAccesses(user, ip, true).catch((e)=>this.logger.error(`${this.validateUser.name} : ${e}`)); return user; } async checkAuth(uid, password) { const servers = _configenvironment.configuration.auth.ldap.servers; const bindUserDN = `${_configenvironment.configuration.auth.ldap.loginAttribute}=${uid},${_configenvironment.configuration.auth.ldap.baseDN}`; let client; let error; for (const s of servers){ client = new _ldapts.Client({ ...this.clientOptions, url: s }); try { await client.bind(bindUserDN, password); return await this.checkAccess(client, uid); } catch (e) { if (e.errors?.length) { for (const err of e.errors){ this.logger.warn(`${this.checkAuth.name} - ${uid} : ${err}`); error = err; } } else { error = e; this.logger.warn(`${this.checkAuth.name} - ${uid} : ${e}`); } if (error instanceof _ldapts.InvalidCredentialsError) { return false; } } finally{ await client.unbind(); } } if (error && _appconstants.CONNECT_ERROR_CODE.has(error.code)) { throw new _common.HttpException('Authentication service connection error', _common.HttpStatus.INTERNAL_SERVER_ERROR); } return false; } async checkAccess(client, uid) { const searchFilter = `(&(${_configenvironment.configuration.auth.ldap.loginAttribute}=${uid})${_configenvironment.configuration.auth.ldap.filter || ''})`; try { const { searchEntries } = await client.search(_configenvironment.configuration.auth.ldap.baseDN, { scope: 'sub', filter: searchFilter, attributes: this.entryAttributes }); for (const entry of searchEntries){ if (entry[_configenvironment.configuration.auth.ldap.loginAttribute] === uid) { if (Array.isArray(entry.mail)) { // handles the case of multiple emails, keep the first entry.mail = entry.mail[0]; } return entry; } } return false; } catch (e) { this.logger.warn(`${this.checkAccess.name} - ${uid} : ${e}`); return false; } } async updateOrCreateUser(identity, user) { if (user === null) { return this.adminUsersManager.createUserOrGuest(identity, _user.USER_ROLE.USER); } else { if (identity.login !== user.login) { this.logger.error(`${this.updateOrCreateUser.name} - user id mismatch : ${identity.login} !== ${user.login}`); throw new _common.HttpException('Account matching error', _common.HttpStatus.FORBIDDEN); } // check if user information has changed const identityHasChanged = Object.fromEntries((await Promise.all(Object.keys(identity).map(async (key)=>{ if (key === 'password') { const isSame = await (0, _functions.comparePassword)(identity[key], user.password); return isSame ? null : [ key, identity[key] ]; } return identity[key] !== user[key] ? [ key, identity[key] ] : null; }))).filter(Boolean)); if (Object.keys(identityHasChanged).length > 0) { try { await this.adminUsersManager.updateUserOrGuest(user.id, identityHasChanged); if (identityHasChanged?.password) { delete identityHasChanged.password; } Object.assign(user, identityHasChanged); } catch (e) { this.logger.warn(`${this.updateOrCreateUser.name} - unable to update user *${user.login}* : ${e}`); } } await user.makePaths(); return user; } } constructor(usersManager, adminUsersManager){ this.usersManager = usersManager; this.adminUsersManager = adminUsersManager; this.logger = new _common.Logger(AuthMethodLdapService.name); this.entryAttributes = [ 'uid', 'mail', 'cn' ]; this.clientOptions = { timeout: 6000, connectTimeout: 6000, url: '' }; } }; AuthMethodLdapService = _ts_decorate([ (0, _common.Injectable)(), _ts_metadata("design:type", Function), _ts_metadata("design:paramtypes", [ typeof _usersmanagerservice.UsersManager === "undefined" ? Object : _usersmanagerservice.UsersManager, typeof _adminusersmanagerservice.AdminUsersManager === "undefined" ? Object : _adminusersmanagerservice.AdminUsersManager ]) ], AuthMethodLdapService); //# sourceMappingURL=auth-method-ldap.service.js.map