From 6438a5e48fa648503ae5e452d539d6ebd20a2ebb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Michaud?= <clement.michaud34@gmail.com>
Date: Sat, 25 Aug 2018 19:22:48 +0200
Subject: [PATCH] Fix ECONNRESET when LDAP queries fail. (#261)

This commit should fix #225.

In order to avoid stalling LDAP connections, Authelia creates new
sessions for each set of queries bound to one authentication, i.e.,
one session for authentication, emails retrieval and groups
retrieval.
Before this commit, a failing query was preventing the session to
be closed (unbind was not called). Now, unbind is always called
whatever the outcome of the query.

I took the opportunity of this commit to refactor LDAP client in
order to prepare the work on users database stored in a file.
(#233)
---
 package-lock.json                             |   6 +-
 package.json                                  |   2 +-
 server/src/lib/ServerVariables.ts             |   8 +-
 server/src/lib/ServerVariablesInitializer.ts  |  31 +-
 .../lib/ServerVariablesMockBuilder.spec.ts    |  16 +-
 server/src/lib/ldap/Authenticator.spec.ts     | 135 ------
 server/src/lib/ldap/Authenticator.ts          |  49 ---
 server/src/lib/ldap/AuthenticatorStub.spec.ts |  16 -
 server/src/lib/ldap/ClientFactory.ts          |  27 --
 server/src/lib/ldap/ClientFactoryStub.spec.ts |  16 -
 .../src/lib/ldap/EmailsAndGroupsRetriever.ts  |  46 ---
 server/src/lib/ldap/EmailsRetriever.spec.ts   |  81 ----
 server/src/lib/ldap/EmailsRetriever.ts        |  39 --
 .../src/lib/ldap/EmailsRetrieverStub.spec.ts  |  16 -
 server/src/lib/ldap/IAuthenticator.ts         |   6 -
 server/src/lib/ldap/IClientFactory.ts         |   6 -
 server/src/lib/ldap/IEmailsRetriever.ts       |   6 -
 server/src/lib/ldap/ILdapClient.ts            |  10 -
 server/src/lib/ldap/ILdapClientFactory.ts     |   6 -
 server/src/lib/ldap/IPasswordUpdater.ts       |   5 -
 .../src/lib/ldap/{IClient.ts => ISession.ts}  |   3 +-
 server/src/lib/ldap/ISessionFactory.ts        |   6 +
 server/src/lib/ldap/IUsersDatabase.ts         |   9 +
 server/src/lib/ldap/InputsSanitizer.spec.ts   |  25 --
 server/src/lib/ldap/LdapClientFactory.ts      |  20 -
 server/src/lib/ldap/LdapUsersDatabase.spec.ts | 386 ++++++++++++++++++
 server/src/lib/ldap/LdapUsersDatabase.ts      | 106 +++++
 server/src/lib/ldap/PasswordUpdater.spec.ts   |  83 ----
 server/src/lib/ldap/PasswordUpdater.ts        |  38 --
 .../src/lib/ldap/PasswordUpdaterStub.spec.ts  |  16 -
 ...izedClient.spec.ts => SafeSession.spec.ts} |  10 +-
 .../{SanitizedClient.ts => SafeSession.ts}    |  33 +-
 server/src/lib/ldap/Sanitizer.spec.ts         |  25 ++
 .../ldap/{InputsSanitizer.ts => Sanitizer.ts} |   2 +-
 .../ldap/{Client.spec.ts => Session.spec.ts}  |  38 +-
 server/src/lib/ldap/{Client.ts => Session.ts} |  27 +-
 server/src/lib/ldap/SessionFactory.ts         |  37 ++
 .../src/lib/ldap/SessionFactoryStub.spec.ts   |  16 +
 ...ClientStub.spec.ts => SessionStub.spec.ts} |  20 +-
 server/src/lib/ldap/UsersDatabaseStub.spec.ts |  35 ++
 .../{LdapClient.ts => connector/Connector.ts} |  42 +-
 .../lib/ldap/connector/ConnectorFactory.ts    |  18 +
 .../ConnectorFactoryStub.spec.ts}             |   8 +-
 .../ConnectorStub.spec.ts}                    |   4 +-
 server/src/lib/ldap/connector/IConnector.ts   |  10 +
 .../lib/ldap/connector/IConnectorFactory.ts   |   5 +
 .../src/lib/routes/firstfactor/post.spec.ts   |   8 +-
 server/src/lib/routes/firstfactor/post.ts     |   4 +-
 .../routes/password-reset/form/post.spec.ts   |   5 +-
 .../lib/routes/password-reset/form/post.ts    |   2 +-
 .../identity/PasswordResetHandler.spec.ts     |  11 +-
 .../identity/PasswordResetHandler.ts          |  10 +-
 server/src/lib/routes/verify/get.spec.ts      |  53 +--
 .../src/lib/routes/verify/get_basic_auth.ts   |  77 ++--
 server/src/lib/web_server/RestApi.ts          |   2 +-
 55 files changed, 843 insertions(+), 878 deletions(-)
 delete mode 100644 server/src/lib/ldap/Authenticator.spec.ts
 delete mode 100644 server/src/lib/ldap/Authenticator.ts
 delete mode 100644 server/src/lib/ldap/AuthenticatorStub.spec.ts
 delete mode 100644 server/src/lib/ldap/ClientFactory.ts
 delete mode 100644 server/src/lib/ldap/ClientFactoryStub.spec.ts
 delete mode 100644 server/src/lib/ldap/EmailsAndGroupsRetriever.ts
 delete mode 100644 server/src/lib/ldap/EmailsRetriever.spec.ts
 delete mode 100644 server/src/lib/ldap/EmailsRetriever.ts
 delete mode 100644 server/src/lib/ldap/EmailsRetrieverStub.spec.ts
 delete mode 100644 server/src/lib/ldap/IAuthenticator.ts
 delete mode 100644 server/src/lib/ldap/IClientFactory.ts
 delete mode 100644 server/src/lib/ldap/IEmailsRetriever.ts
 delete mode 100644 server/src/lib/ldap/ILdapClient.ts
 delete mode 100644 server/src/lib/ldap/ILdapClientFactory.ts
 delete mode 100644 server/src/lib/ldap/IPasswordUpdater.ts
 rename server/src/lib/ldap/{IClient.ts => ISession.ts} (94%)
 create mode 100644 server/src/lib/ldap/ISessionFactory.ts
 create mode 100644 server/src/lib/ldap/IUsersDatabase.ts
 delete mode 100644 server/src/lib/ldap/InputsSanitizer.spec.ts
 delete mode 100644 server/src/lib/ldap/LdapClientFactory.ts
 create mode 100644 server/src/lib/ldap/LdapUsersDatabase.spec.ts
 create mode 100644 server/src/lib/ldap/LdapUsersDatabase.ts
 delete mode 100644 server/src/lib/ldap/PasswordUpdater.spec.ts
 delete mode 100644 server/src/lib/ldap/PasswordUpdater.ts
 delete mode 100644 server/src/lib/ldap/PasswordUpdaterStub.spec.ts
 rename server/src/lib/ldap/{SanitizedClient.spec.ts => SafeSession.spec.ts} (92%)
 rename server/src/lib/ldap/{SanitizedClient.ts => SafeSession.ts} (52%)
 create mode 100644 server/src/lib/ldap/Sanitizer.spec.ts
 rename server/src/lib/ldap/{InputsSanitizer.ts => Sanitizer.ts} (96%)
 rename server/src/lib/ldap/{Client.spec.ts => Session.spec.ts} (73%)
 rename server/src/lib/ldap/{Client.ts => Session.ts} (87%)
 create mode 100644 server/src/lib/ldap/SessionFactory.ts
 create mode 100644 server/src/lib/ldap/SessionFactoryStub.spec.ts
 rename server/src/lib/ldap/{ClientStub.spec.ts => SessionStub.spec.ts} (67%)
 create mode 100644 server/src/lib/ldap/UsersDatabaseStub.spec.ts
 rename server/src/lib/ldap/{LdapClient.ts => connector/Connector.ts} (50%)
 create mode 100644 server/src/lib/ldap/connector/ConnectorFactory.ts
 rename server/src/lib/ldap/{LdapClientFactoryStub.spec.ts => connector/ConnectorFactoryStub.spec.ts} (51%)
 rename server/src/lib/ldap/{LdapClientStub.spec.ts => connector/ConnectorStub.spec.ts} (89%)
 create mode 100644 server/src/lib/ldap/connector/IConnector.ts
 create mode 100644 server/src/lib/ldap/connector/IConnectorFactory.ts

diff --git a/package-lock.json b/package-lock.json
index bf629abe..0e2a1ce3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -958,9 +958,9 @@
       }
     },
     "bluebird": {
-      "version": "3.5.1",
-      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
-      "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz",
+      "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw="
     },
     "bn.js": {
       "version": "4.11.8",
diff --git a/package.json b/package.json
index 2f99964a..4b3060f9 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,7 @@
     "@types/helmet": "0.0.37",
     "@types/jquery": "^3.3.1",
     "@types/jsdom": "^11.0.4",
-    "@types/ldapjs": "^1.0.2",
+    "@types/ldapjs": "^1.0.3",
     "@types/mocha": "^5.0.0",
     "@types/mockdate": "^2.0.0",
     "@types/mongodb": "^3.0.9",
diff --git a/server/src/lib/ServerVariables.ts b/server/src/lib/ServerVariables.ts
index cdd5285e..e78b48a9 100644
--- a/server/src/lib/ServerVariables.ts
+++ b/server/src/lib/ServerVariables.ts
@@ -1,7 +1,4 @@
 import { IRequestLogger } from "./logging/IRequestLogger";
-import { IAuthenticator } from "./ldap/IAuthenticator";
-import { IPasswordUpdater } from "./ldap/IPasswordUpdater";
-import { IEmailsRetriever } from "./ldap/IEmailsRetriever";
 import { ITotpHandler } from "./authentication/totp/ITotpHandler";
 import { IU2fHandler } from "./authentication/u2f/IU2fHandler";
 import { IUserDataStore } from "./storage/IUserDataStore";
@@ -9,12 +6,11 @@ import { INotifier } from "./notifiers/INotifier";
 import { IRegulator } from "./regulation/IRegulator";
 import { Configuration } from "./configuration/schema/Configuration";
 import { IAccessController } from "./access_control/IAccessController";
+import { IUsersDatabase } from "./ldap/IUsersDatabase";
 
 export interface ServerVariables {
   logger: IRequestLogger;
-  ldapAuthenticator: IAuthenticator;
-  ldapPasswordUpdater: IPasswordUpdater;
-  ldapEmailsRetriever: IEmailsRetriever;
+  usersDatabase: IUsersDatabase;
   totpHandler: ITotpHandler;
   u2f: IU2fHandler;
   userDataStore: IUserDataStore;
diff --git a/server/src/lib/ServerVariablesInitializer.ts b/server/src/lib/ServerVariablesInitializer.ts
index 0f720996..432352e8 100644
--- a/server/src/lib/ServerVariablesInitializer.ts
+++ b/server/src/lib/ServerVariablesInitializer.ts
@@ -7,19 +7,12 @@ import Nodemailer = require("nodemailer");
 import { IRequestLogger } from "./logging/IRequestLogger";
 import { RequestLogger } from "./logging/RequestLogger";
 
-import { IAuthenticator } from "./ldap/IAuthenticator";
-import { IPasswordUpdater } from "./ldap/IPasswordUpdater";
-import { IEmailsRetriever } from "./ldap/IEmailsRetriever";
-import { Authenticator } from "./ldap/Authenticator";
-import { PasswordUpdater } from "./ldap/PasswordUpdater";
-import { EmailsRetriever } from "./ldap/EmailsRetriever";
-import { ClientFactory } from "./ldap/ClientFactory";
-import { LdapClientFactory } from "./ldap/LdapClientFactory";
-
 import { TotpHandler } from "./authentication/totp/TotpHandler";
 import { ITotpHandler } from "./authentication/totp/ITotpHandler";
 import { NotifierFactory } from "./notifiers/NotifierFactory";
 import { MailSenderBuilder } from "./notifiers/MailSenderBuilder";
+import { LdapUsersDatabase } from "./ldap/LdapUsersDatabase";
+import { ConnectorFactory } from "./ldap/connector/ConnectorFactory";
 
 import { IUserDataStore } from "./storage/IUserDataStore";
 import { UserDataStore } from "./storage/UserDataStore";
@@ -39,6 +32,7 @@ import { ServerVariables } from "./ServerVariables";
 import { MethodCalculator } from "./authentication/MethodCalculator";
 import { MongoClient } from "./connectors/mongo/MongoClient";
 import { IGlobalLogger } from "./logging/IGlobalLogger";
+import { SessionFactory } from "./ldap/SessionFactory";
 
 class UserDataStoreFactory {
   static create(config: Configuration.Configuration, globalLogger: IGlobalLogger): BluebirdPromise<UserDataStore> {
@@ -72,13 +66,14 @@ export class ServerVariablesInitializer {
 
     const mailSenderBuilder = new MailSenderBuilder(Nodemailer);
     const notifier = NotifierFactory.build(config.notifier, mailSenderBuilder);
-    const ldapClientFactory = new LdapClientFactory(config.ldap, deps.ldapjs);
-    const clientFactory = new ClientFactory(config.ldap, ldapClientFactory,
-      deps.winston);
-
-    const ldapAuthenticator = new Authenticator(config.ldap, clientFactory);
-    const ldapPasswordUpdater = new PasswordUpdater(config.ldap, clientFactory);
-    const ldapEmailsRetriever = new EmailsRetriever(config.ldap, clientFactory);
+    const ldapUsersDatabase = new LdapUsersDatabase(
+      new SessionFactory(
+        config.ldap,
+        new ConnectorFactory(config.ldap, deps.ldapjs),
+        deps.winston
+      ),
+      config.ldap
+    );
     const accessController = new AccessController(config.access_control, deps.winston);
     const totpHandler = new TotpHandler(deps.speakeasy);
 
@@ -90,9 +85,7 @@ export class ServerVariablesInitializer {
         const variables: ServerVariables = {
           accessController: accessController,
           config: config,
-          ldapAuthenticator: ldapAuthenticator,
-          ldapPasswordUpdater: ldapPasswordUpdater,
-          ldapEmailsRetriever: ldapEmailsRetriever,
+          usersDatabase: ldapUsersDatabase,
           logger: requestLogger,
           notifier: notifier,
           regulator: regulator,
diff --git a/server/src/lib/ServerVariablesMockBuilder.spec.ts b/server/src/lib/ServerVariablesMockBuilder.spec.ts
index a26218e1..a0e41797 100644
--- a/server/src/lib/ServerVariablesMockBuilder.spec.ts
+++ b/server/src/lib/ServerVariablesMockBuilder.spec.ts
@@ -1,9 +1,7 @@
 import { ServerVariables } from "./ServerVariables";
 
 import { Configuration } from "./configuration/schema/Configuration";
-import { AuthenticatorStub } from "./ldap/AuthenticatorStub.spec";
-import { EmailsRetrieverStub } from "./ldap/EmailsRetrieverStub.spec";
-import { PasswordUpdaterStub } from "./ldap/PasswordUpdaterStub.spec";
+import { UsersDatabaseStub } from "./ldap/UsersDatabaseStub.spec";
 import { AccessControllerStub } from "./access_control/AccessControllerStub.spec";
 import { RequestLoggerStub } from "./logging/RequestLoggerStub.spec";
 import { NotifierStub } from "./notifiers/NotifierStub.spec";
@@ -15,9 +13,7 @@ import { U2fHandlerStub } from "./authentication/u2f/U2fHandlerStub.spec";
 export interface ServerVariablesMock {
   accessController: AccessControllerStub;
   config: Configuration;
-  ldapAuthenticator: AuthenticatorStub;
-  ldapEmailsRetriever: EmailsRetrieverStub;
-  ldapPasswordUpdater: PasswordUpdaterStub;
+  usersDatabase: UsersDatabaseStub;
   logger: RequestLoggerStub;
   notifier: NotifierStub;
   regulator: RegulatorStub;
@@ -64,9 +60,7 @@ export class ServerVariablesMockBuilder {
         },
         storage: {}
       },
-      ldapAuthenticator: new AuthenticatorStub(),
-      ldapEmailsRetriever: new EmailsRetrieverStub(),
-      ldapPasswordUpdater: new PasswordUpdaterStub(),
+      usersDatabase: new UsersDatabaseStub(),
       logger: new RequestLoggerStub(enableLogging),
       notifier: new NotifierStub(),
       regulator: new RegulatorStub(),
@@ -77,9 +71,7 @@ export class ServerVariablesMockBuilder {
     const vars: ServerVariables = {
       accessController: mocks.accessController,
       config: mocks.config,
-      ldapAuthenticator: mocks.ldapAuthenticator,
-      ldapEmailsRetriever: mocks.ldapEmailsRetriever,
-      ldapPasswordUpdater: mocks.ldapPasswordUpdater,
+      usersDatabase: mocks.usersDatabase,
       logger: mocks.logger,
       notifier: mocks.notifier,
       regulator: mocks.regulator,
diff --git a/server/src/lib/ldap/Authenticator.spec.ts b/server/src/lib/ldap/Authenticator.spec.ts
deleted file mode 100644
index 2a229756..00000000
--- a/server/src/lib/ldap/Authenticator.spec.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-
-import { Authenticator } from "./Authenticator";
-import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
-
-import Sinon = require("sinon");
-import BluebirdPromise = require("bluebird");
-import Assert = require("assert");
-
-import { ClientFactoryStub } from "./ClientFactoryStub.spec";
-import { ClientStub } from "./ClientStub.spec";
-
-
-describe("ldap/Authenticator", function () {
-  const USERNAME = "username";
-  const PASSWORD = "password";
-
-  const ADMIN_USER_DN = "cn=admin,dc=example,dc=com";
-  const ADMIN_PASSWORD = "admin_password";
-
-  let clientFactoryStub: ClientFactoryStub;
-  let adminClientStub: ClientStub;
-  let userClientStub: ClientStub;
-
-  let authenticator: Authenticator;
-  let ldapConfig: LdapConfiguration;
-
-  beforeEach(function () {
-    clientFactoryStub = new ClientFactoryStub();
-    adminClientStub = new ClientStub();
-    userClientStub = new ClientStub();
-
-    ldapConfig = {
-      url: "http://localhost:324",
-      additional_users_dn: "ou=users",
-      additional_groups_dn: "ou=groups",
-      base_dn: "dc=example,dc=com",
-      users_filter: "cn={0}",
-      groups_filter: "member={0}",
-      mail_attribute: "mail",
-      group_name_attribute: "cn",
-      user: ADMIN_USER_DN,
-      password: ADMIN_PASSWORD
-    };
-
-    authenticator = new Authenticator(ldapConfig, clientFactoryStub);
-  });
-
-  describe("success", function () {
-    it("should bind the user if good credentials provided", function () {
-      clientFactoryStub.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD)
-        .returns(adminClientStub);
-      clientFactoryStub.createStub.withArgs("cn=" + USERNAME + ",ou=users,dc=example,dc=com", PASSWORD)
-        .returns(userClientStub);
-
-      // admin connects successfully
-      adminClientStub.openStub.returns(BluebirdPromise.resolve());
-      adminClientStub.closeStub.returns(BluebirdPromise.resolve());
-
-      // admin search for user dn of user
-      adminClientStub.searchUserDnStub.withArgs(USERNAME)
-        .returns(BluebirdPromise.resolve("cn=" + USERNAME + ",ou=users,dc=example,dc=com"));
-
-      // user connects successfully
-      userClientStub.openStub.returns(BluebirdPromise.resolve());
-      userClientStub.closeStub.returns(BluebirdPromise.resolve());
-
-      // admin retrieves emails and groups of user
-      adminClientStub.searchEmailsStub.returns(BluebirdPromise.resolve(["group1"]));
-      adminClientStub.searchGroupsStub.returns(BluebirdPromise.resolve(["user@example.com"]));
-
-      return authenticator.authenticate(USERNAME, PASSWORD);
-    });
-  });
-
-  describe("failure", function () {
-    it("should not bind the user if wrong credentials provided", function () {
-      clientFactoryStub.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD)
-        .returns(adminClientStub);
-      clientFactoryStub.createStub.withArgs("cn=" + USERNAME + ",ou=users,dc=example,dc=com", PASSWORD)
-        .returns(userClientStub);
-
-      // admin connects successfully
-      adminClientStub.openStub.returns(BluebirdPromise.resolve());
-      adminClientStub.closeStub.returns(BluebirdPromise.resolve());
-
-      // admin search for user dn of user
-      adminClientStub.searchUserDnStub.withArgs(USERNAME)
-        .returns(BluebirdPromise.resolve("cn=" + USERNAME + ",ou=users,dc=example,dc=com"));
-
-      // user connects successfully
-      userClientStub.openStub.rejects(new Error("Error while binding"));
-      userClientStub.closeStub.returns(BluebirdPromise.resolve());
-
-      return authenticator.authenticate(USERNAME, PASSWORD)
-        .then(function () {
-          return BluebirdPromise.reject("Should not be here!");
-        })
-        .catch(function () {
-          return BluebirdPromise.resolve();
-        });
-    });
-
-    it("should not bind the user if search of emails or group fails", function () {
-      clientFactoryStub.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD)
-        .returns(adminClientStub);
-      clientFactoryStub.createStub.withArgs("cn=" + USERNAME + ",ou=users,dc=example,dc=com", PASSWORD)
-        .returns(userClientStub);
-
-      // admin connects successfully
-      adminClientStub.openStub.returns(BluebirdPromise.resolve());
-      adminClientStub.closeStub.returns(BluebirdPromise.resolve());
-
-      // admin search for user dn of user
-      adminClientStub.searchUserDnStub.withArgs(USERNAME)
-        .returns(BluebirdPromise.resolve("cn=" + USERNAME + ",ou=users,dc=example,dc=com"));
-
-      // user connects successfully
-      userClientStub.openStub.returns(BluebirdPromise.resolve());
-      userClientStub.closeStub.returns(BluebirdPromise.resolve());
-
-      adminClientStub.searchEmailsStub.returns(BluebirdPromise.resolve(["group1"]));
-      // admin retrieves emails and groups of user
-      adminClientStub.searchGroupsStub
-        .rejects(new Error("Error while retrieving emails and groups"));
-
-      return authenticator.authenticate(USERNAME, PASSWORD)
-        .then(function () {
-          return BluebirdPromise.reject("Should not be here!");
-        })
-        .catch(function () {
-          return BluebirdPromise.resolve();
-        });
-    });
-  });
-});
\ No newline at end of file
diff --git a/server/src/lib/ldap/Authenticator.ts b/server/src/lib/ldap/Authenticator.ts
deleted file mode 100644
index d804ee59..00000000
--- a/server/src/lib/ldap/Authenticator.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import BluebirdPromise = require("bluebird");
-import exceptions = require("../Exceptions");
-import ldapjs = require("ldapjs");
-import { IClient } from "./IClient";
-import { IClientFactory } from "./IClientFactory";
-import { GroupsAndEmails } from "./IClient";
-
-import { IAuthenticator } from "./IAuthenticator";
-import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
-import { EmailsAndGroupsRetriever } from "./EmailsAndGroupsRetriever";
-
-
-export class Authenticator implements IAuthenticator {
-  private options: LdapConfiguration;
-  private clientFactory: IClientFactory;
-
-  constructor(options: LdapConfiguration, clientFactory: IClientFactory) {
-    this.options = options;
-    this.clientFactory = clientFactory;
-  }
-
-  authenticate(username: string, password: string): BluebirdPromise<GroupsAndEmails> {
-    const that = this;
-    let userClient: IClient;
-    const adminClient = this.clientFactory.create(this.options.user, this.options.password);
-    const emailsAndGroupsRetriever = new EmailsAndGroupsRetriever(this.options, this.clientFactory);
-
-    return adminClient.open()
-      .then(function () {
-        return adminClient.searchUserDn(username);
-      })
-      .then(function (userDN: string) {
-        userClient = that.clientFactory.create(userDN, password);
-        return userClient.open();
-      })
-      .then(function () {
-        return userClient.close();
-      })
-      .then(function () {
-        return emailsAndGroupsRetriever.retrieve(username);
-      })
-      .then(function (groupsAndEmails: GroupsAndEmails) {
-        return BluebirdPromise.resolve(groupsAndEmails);
-      })
-      .error(function (err: Error) {
-        return BluebirdPromise.reject(new exceptions.LdapError(err.message));
-      });
-  }
-}
\ No newline at end of file
diff --git a/server/src/lib/ldap/AuthenticatorStub.spec.ts b/server/src/lib/ldap/AuthenticatorStub.spec.ts
deleted file mode 100644
index 1863e45b..00000000
--- a/server/src/lib/ldap/AuthenticatorStub.spec.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import BluebirdPromise = require("bluebird");
-import { IAuthenticator } from "../../../src/lib/ldap/IAuthenticator";
-import { GroupsAndEmails } from "./IClient";
-import Sinon = require("sinon");
-
-export class AuthenticatorStub implements IAuthenticator {
-  authenticateStub: Sinon.SinonStub;
-
-  constructor() {
-    this.authenticateStub = Sinon.stub();
-  }
-
-  authenticate(username: string, password: string): BluebirdPromise<GroupsAndEmails> {
-    return this.authenticateStub(username, password);
-  }
-}
\ No newline at end of file
diff --git a/server/src/lib/ldap/ClientFactory.ts b/server/src/lib/ldap/ClientFactory.ts
deleted file mode 100644
index 539e37f8..00000000
--- a/server/src/lib/ldap/ClientFactory.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { IClientFactory } from "./IClientFactory";
-import { IClient } from "./IClient";
-import { Client } from "./Client";
-import { SanitizedClient } from "./SanitizedClient";
-import { ILdapClientFactory } from "./ILdapClientFactory";
-import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
-import Ldapjs = require("ldapjs");
-import Winston = require("winston");
-
-export class ClientFactory implements IClientFactory {
-  private config: LdapConfiguration;
-  private ldapClientFactory: ILdapClientFactory;
-  private logger: typeof Winston;
-
-  constructor(ldapConfiguration: LdapConfiguration,
-    ldapClientFactory: ILdapClientFactory,
-    logger: typeof Winston) {
-    this.config = ldapConfiguration;
-    this.ldapClientFactory = ldapClientFactory;
-    this.logger = logger;
-  }
-
-  create(userDN: string, password: string): IClient {
-    return new SanitizedClient(new Client(userDN, password,
-      this.config, this.ldapClientFactory, this.logger));
-  }
-}
\ No newline at end of file
diff --git a/server/src/lib/ldap/ClientFactoryStub.spec.ts b/server/src/lib/ldap/ClientFactoryStub.spec.ts
deleted file mode 100644
index 2b3afe8e..00000000
--- a/server/src/lib/ldap/ClientFactoryStub.spec.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-
-import { IClient } from "../../../src/lib/ldap/IClient";
-import { IClientFactory } from "../../../src/lib/ldap/IClientFactory";
-import Sinon = require("sinon");
-
-export class ClientFactoryStub implements IClientFactory {
-  createStub: Sinon.SinonStub;
-
-  constructor() {
-    this.createStub = Sinon.stub();
-  }
-
-  create(userDN: string, password: string): IClient {
-    return this.createStub(userDN, password);
-  }
-}
\ No newline at end of file
diff --git a/server/src/lib/ldap/EmailsAndGroupsRetriever.ts b/server/src/lib/ldap/EmailsAndGroupsRetriever.ts
deleted file mode 100644
index 0c4f1441..00000000
--- a/server/src/lib/ldap/EmailsAndGroupsRetriever.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import BluebirdPromise = require("bluebird");
-import exceptions = require("../Exceptions");
-import ldapjs = require("ldapjs");
-import { Client } from "./Client";
-import { IClientFactory } from "./IClientFactory";
-import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
-import { GroupsAndEmails } from "./IClient";
-
-
-export class EmailsAndGroupsRetriever {
-  private options: LdapConfiguration;
-  private clientFactory: IClientFactory;
-
-  constructor(options: LdapConfiguration, clientFactory: IClientFactory) {
-    this.options = options;
-    this.clientFactory = clientFactory;
-  }
-
-  retrieve(username: string): BluebirdPromise<GroupsAndEmails> {
-    const adminClient = this.clientFactory.create(this.options.user, this.options.password);
-    let emails: string[];
-    let groups: string[];
-
-    return adminClient.open()
-      .then(function () {
-        return adminClient.searchEmails(username);
-      })
-      .then(function (emails_: string[]) {
-        emails = emails_;
-        return adminClient.searchGroups(username);
-      })
-      .then(function (groups_: string[]) {
-        groups = groups_;
-        return adminClient.close();
-      })
-      .then(function () {
-        return BluebirdPromise.resolve({
-          emails: emails,
-          groups: groups
-        });
-      })
-      .error(function (err: Error) {
-        return BluebirdPromise.reject(new exceptions.LdapError("Failed during emails and groups retrieval: " + err.message));
-      });
-  }
-}
diff --git a/server/src/lib/ldap/EmailsRetriever.spec.ts b/server/src/lib/ldap/EmailsRetriever.spec.ts
deleted file mode 100644
index 83bd5e77..00000000
--- a/server/src/lib/ldap/EmailsRetriever.spec.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-
-import { EmailsRetriever } from "./EmailsRetriever";
-import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
-
-import Sinon = require("sinon");
-import BluebirdPromise = require("bluebird");
-import Assert = require("assert");
-
-import { ClientFactoryStub } from "./ClientFactoryStub.spec";
-import { ClientStub } from "./ClientStub.spec";
-
-describe("ldap/EmailsRetriever", function () {
-  const USERNAME = "username";
-  const ADMIN_USER_DN = "cn=admin,dc=example,dc=com";
-  const ADMIN_PASSWORD = "password";
-
-  let clientFactoryStub: ClientFactoryStub;
-  let adminClientStub: ClientStub;
-
-  let emailsRetriever: EmailsRetriever;
-  let ldapConfig: LdapConfiguration;
-
-  beforeEach(function () {
-    clientFactoryStub = new ClientFactoryStub();
-    adminClientStub = new ClientStub();
-
-    ldapConfig = {
-      url: "http://ldap",
-      user: ADMIN_USER_DN,
-      password: ADMIN_PASSWORD,
-      additional_users_dn: "ou=users",
-      additional_groups_dn: "ou=groups",
-      base_dn: "dc=example,dc=com",
-      group_name_attribute: "cn",
-      groups_filter: "cn={0}",
-      mail_attribute: "mail",
-      users_filter: "cn={0}"
-    };
-
-    emailsRetriever = new EmailsRetriever(ldapConfig, clientFactoryStub);
-  });
-
-  describe("success", function () {
-    it("should retrieve emails successfully", function () {
-      clientFactoryStub.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD)
-        .returns(adminClientStub);
-
-      // admin connects successfully
-      adminClientStub.openStub.returns(BluebirdPromise.resolve());
-      adminClientStub.closeStub.returns(BluebirdPromise.resolve());
-
-      adminClientStub.searchEmailsStub.withArgs(USERNAME)
-        .returns(BluebirdPromise.resolve(["user@example.com"]));
-
-      return emailsRetriever.retrieve(USERNAME);
-    });
-  });
-
-  describe("failure", function () {
-    it("should fail retrieving emails when search operation fails",
-      function () {
-        clientFactoryStub.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD)
-          .returns(adminClientStub);
-
-        // admin connects successfully
-        adminClientStub.openStub.returns(BluebirdPromise.resolve());
-        adminClientStub.closeStub.returns(BluebirdPromise.resolve());
-
-        adminClientStub.searchEmailsStub.withArgs(USERNAME)
-          .rejects(new Error("Error while searching emails"));
-
-        return emailsRetriever.retrieve(USERNAME)
-          .then(function () {
-            return BluebirdPromise.reject(new Error("Should not be here"));
-          })
-          .catch(function () {
-            return BluebirdPromise.resolve();
-          });
-      });
-  });
-});
\ No newline at end of file
diff --git a/server/src/lib/ldap/EmailsRetriever.ts b/server/src/lib/ldap/EmailsRetriever.ts
deleted file mode 100644
index 21a38ed1..00000000
--- a/server/src/lib/ldap/EmailsRetriever.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import BluebirdPromise = require("bluebird");
-import exceptions = require("../Exceptions");
-import ldapjs = require("ldapjs");
-import { Client } from "./Client";
-
-import { IClientFactory } from "./IClientFactory";
-import { IEmailsRetriever } from "./IEmailsRetriever";
-import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
-
-
-export class EmailsRetriever implements IEmailsRetriever {
-  private options: LdapConfiguration;
-  private clientFactory: IClientFactory;
-
-  constructor(options: LdapConfiguration, clientFactory: IClientFactory) {
-    this.options = options;
-    this.clientFactory = clientFactory;
-  }
-
-  retrieve(username: string): BluebirdPromise<string[]> {
-    const adminClient = this.clientFactory.create(this.options.user, this.options.password);
-    let emails: string[];
-
-    return adminClient.open()
-      .then(function () {
-        return adminClient.searchEmails(username);
-      })
-      .then(function (emails_: string[]) {
-        emails = emails_;
-        return adminClient.close();
-      })
-      .then(function () {
-        return BluebirdPromise.resolve(emails);
-      })
-      .catch(function (err: Error) {
-        return BluebirdPromise.reject(new exceptions.LdapError("Failed during email retrieval: " + err.message));
-      });
-  }
-}
diff --git a/server/src/lib/ldap/EmailsRetrieverStub.spec.ts b/server/src/lib/ldap/EmailsRetrieverStub.spec.ts
deleted file mode 100644
index 442edea0..00000000
--- a/server/src/lib/ldap/EmailsRetrieverStub.spec.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import BluebirdPromise = require("bluebird");
-import { IClient } from "./IClient";
-import { IEmailsRetriever } from "./IEmailsRetriever";
-import Sinon = require("sinon");
-
-export class EmailsRetrieverStub implements IEmailsRetriever {
-  retrieveStub: Sinon.SinonStub;
-
-  constructor() {
-    this.retrieveStub = Sinon.stub();
-  }
-
-  retrieve(username: string, client?: IClient): BluebirdPromise<string[]> {
-    return this.retrieveStub(username, client);
-  }
-}
\ No newline at end of file
diff --git a/server/src/lib/ldap/IAuthenticator.ts b/server/src/lib/ldap/IAuthenticator.ts
deleted file mode 100644
index b1813ac2..00000000
--- a/server/src/lib/ldap/IAuthenticator.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import BluebirdPromise = require("bluebird");
-import { GroupsAndEmails } from "./IClient";
-
-export interface IAuthenticator {
-  authenticate(username: string, password: string): BluebirdPromise<GroupsAndEmails>;
-}
\ No newline at end of file
diff --git a/server/src/lib/ldap/IClientFactory.ts b/server/src/lib/ldap/IClientFactory.ts
deleted file mode 100644
index 19a6a656..00000000
--- a/server/src/lib/ldap/IClientFactory.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-
-import { IClient } from "./IClient";
-
-export interface IClientFactory {
-  create(userDN: string, password: string): IClient;
-}
\ No newline at end of file
diff --git a/server/src/lib/ldap/IEmailsRetriever.ts b/server/src/lib/ldap/IEmailsRetriever.ts
deleted file mode 100644
index 65ae2191..00000000
--- a/server/src/lib/ldap/IEmailsRetriever.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import BluebirdPromise = require("bluebird");
-import { IClient } from "./IClient";
-
-export interface IEmailsRetriever {
-  retrieve(username: string, client?: IClient): BluebirdPromise<string[]>;
-}
\ No newline at end of file
diff --git a/server/src/lib/ldap/ILdapClient.ts b/server/src/lib/ldap/ILdapClient.ts
deleted file mode 100644
index 38824080..00000000
--- a/server/src/lib/ldap/ILdapClient.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-
-import BluebirdPromise = require("bluebird");
-import EventEmitter = require("events");
-
-export interface ILdapClient {
-  bindAsync(username: string, password: string): BluebirdPromise<void>;
-  unbindAsync(): BluebirdPromise<void>;
-  searchAsync(base: string, query: any): BluebirdPromise<any[]>;
-  modifyAsync(dn: string, changeRequest: any): BluebirdPromise<void>;
-}
\ No newline at end of file
diff --git a/server/src/lib/ldap/ILdapClientFactory.ts b/server/src/lib/ldap/ILdapClientFactory.ts
deleted file mode 100644
index be2ce8e4..00000000
--- a/server/src/lib/ldap/ILdapClientFactory.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-
-import { ILdapClient } from "./ILdapClient";
-
-export interface ILdapClientFactory {
-  create(): ILdapClient;
-}
\ No newline at end of file
diff --git a/server/src/lib/ldap/IPasswordUpdater.ts b/server/src/lib/ldap/IPasswordUpdater.ts
deleted file mode 100644
index ff8f3d2c..00000000
--- a/server/src/lib/ldap/IPasswordUpdater.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import BluebirdPromise = require("bluebird");
-
-export interface IPasswordUpdater {
-  updatePassword(username: string, newPassword: string): BluebirdPromise<void>;
-}
\ No newline at end of file
diff --git a/server/src/lib/ldap/IClient.ts b/server/src/lib/ldap/ISession.ts
similarity index 94%
rename from server/src/lib/ldap/IClient.ts
rename to server/src/lib/ldap/ISession.ts
index 55856d67..2cb75726 100644
--- a/server/src/lib/ldap/IClient.ts
+++ b/server/src/lib/ldap/ISession.ts
@@ -6,9 +6,10 @@ export interface GroupsAndEmails {
   emails: string[];
 }
 
-export interface IClient {
+export interface ISession {
   open(): BluebirdPromise<void>;
   close(): BluebirdPromise<void>;
+
   searchUserDn(username: string): BluebirdPromise<string>;
   searchEmails(username: string): BluebirdPromise<string[]>;
   searchGroups(username: string): BluebirdPromise<string[]>;
diff --git a/server/src/lib/ldap/ISessionFactory.ts b/server/src/lib/ldap/ISessionFactory.ts
new file mode 100644
index 00000000..014d1eea
--- /dev/null
+++ b/server/src/lib/ldap/ISessionFactory.ts
@@ -0,0 +1,6 @@
+
+import { ISession } from "./ISession";
+
+export interface ISessionFactory {
+  create(userDN: string, password: string): ISession;
+}
\ No newline at end of file
diff --git a/server/src/lib/ldap/IUsersDatabase.ts b/server/src/lib/ldap/IUsersDatabase.ts
new file mode 100644
index 00000000..7e814b7e
--- /dev/null
+++ b/server/src/lib/ldap/IUsersDatabase.ts
@@ -0,0 +1,9 @@
+import Bluebird = require("bluebird");
+import { GroupsAndEmails } from "./ISession";
+
+export interface IUsersDatabase {
+  checkUserPassword(username: string, password: string): Bluebird<GroupsAndEmails>;
+  getEmails(username: string): Bluebird<string[]>;
+  getGroups(username: string): Bluebird<string[]>;
+  updatePassword(username: string, newPassword: string): Bluebird<void>;
+}
\ No newline at end of file
diff --git a/server/src/lib/ldap/InputsSanitizer.spec.ts b/server/src/lib/ldap/InputsSanitizer.spec.ts
deleted file mode 100644
index 791d390b..00000000
--- a/server/src/lib/ldap/InputsSanitizer.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import Assert = require("assert");
-import { InputsSanitizer } from "./InputsSanitizer";
-
-describe("ldap/InputsSanitizer", function () {
-  it("should fail when special characters are used", function () {
-    Assert.throws(() => { InputsSanitizer.sanitize("ab,c"); }, Error);
-    Assert.throws(() => { InputsSanitizer.sanitize("a\\bc"); }, Error);
-    Assert.throws(() => { InputsSanitizer.sanitize("a'bc"); }, Error);
-    Assert.throws(() => { InputsSanitizer.sanitize("a#bc"); }, Error);
-    Assert.throws(() => { InputsSanitizer.sanitize("a+bc"); }, Error);
-    Assert.throws(() => { InputsSanitizer.sanitize("a<bc"); }, Error);
-    Assert.throws(() => { InputsSanitizer.sanitize("a>bc"); }, Error);
-    Assert.throws(() => { InputsSanitizer.sanitize("a;bc"); }, Error);
-    Assert.throws(() => { InputsSanitizer.sanitize("a\"bc"); }, Error);
-    Assert.throws(() => { InputsSanitizer.sanitize("a=bc"); }, Error);
-  });
-
-  it("should return original string", function () {
-    Assert.equal(InputsSanitizer.sanitize("abcdef"), "abcdef");
-  });
-
-  it("should trim", function () {
-    Assert.throws(() => { InputsSanitizer.sanitize("    abc    "); }, Error);
-  });
-});
diff --git a/server/src/lib/ldap/LdapClientFactory.ts b/server/src/lib/ldap/LdapClientFactory.ts
deleted file mode 100644
index 6e1fe292..00000000
--- a/server/src/lib/ldap/LdapClientFactory.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { ILdapClientFactory } from "./ILdapClientFactory";
-import { ILdapClient } from "./ILdapClient";
-import { LdapClient } from "./LdapClient";
-import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
-
-import Ldapjs = require("ldapjs");
-
-export class LdapClientFactory implements ILdapClientFactory {
-  private config: LdapConfiguration;
-  private ldapjs: typeof Ldapjs;
-
-  constructor(ldapConfiguration: LdapConfiguration, ldapjs: typeof Ldapjs) {
-    this.config = ldapConfiguration;
-    this.ldapjs = ldapjs;
-  }
-
-  create(): ILdapClient {
-    return new LdapClient(this.config.url, this.ldapjs);
-  }
-}
\ No newline at end of file
diff --git a/server/src/lib/ldap/LdapUsersDatabase.spec.ts b/server/src/lib/ldap/LdapUsersDatabase.spec.ts
new file mode 100644
index 00000000..f4a6e630
--- /dev/null
+++ b/server/src/lib/ldap/LdapUsersDatabase.spec.ts
@@ -0,0 +1,386 @@
+import Assert = require("assert");
+import Bluebird = require("bluebird");
+
+import { LdapUsersDatabase } from "./LdapUsersDatabase";
+
+import { SessionFactoryStub } from "./SessionFactoryStub.spec";
+import { SessionStub } from "./SessionStub.spec";
+
+const ADMIN_USER_DN = "cn=admin,dc=example,dc=com";
+const ADMIN_PASSWORD = "password";
+
+describe("ldap/connector/LdapUsersDatabase", function() {
+  let sessionFactory: SessionFactoryStub;
+  let usersDatabase: LdapUsersDatabase;
+
+  const USERNAME = "user";
+  const PASSWORD = "pass";
+  const NEW_PASSWORD = "pass2";
+
+  const LDAP_CONFIG = {
+    url: "http://localhost:324",
+    additional_users_dn: "ou=users",
+    additional_groups_dn: "ou=groups",
+    base_dn: "dc=example,dc=com",
+    users_filter: "cn={0}",
+    groups_filter: "member={0}",
+    mail_attribute: "mail",
+    group_name_attribute: "cn",
+    user: ADMIN_USER_DN,
+    password: ADMIN_PASSWORD
+  };
+
+  beforeEach(function() {
+    sessionFactory = new SessionFactoryStub();
+    usersDatabase = new LdapUsersDatabase(sessionFactory, LDAP_CONFIG);
+  })
+
+  describe("checkUserPassword", function() {
+    it("should return groups and emails when user/password matches", function() {
+      const USER_DN = `cn=${USERNAME},dc=example,dc=com`;
+      const emails = ["email1", "email2"];
+      const groups = ["group1", "group2"];
+
+      const adminSession = new SessionStub();
+      const userSession = new SessionStub();
+
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession);
+      sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession);
+
+      adminSession.openStub.returns(Bluebird.resolve());
+      adminSession.closeStub.returns(Bluebird.resolve());
+      adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN));
+      adminSession.searchEmailsStub.withArgs(USERNAME).returns(Bluebird.resolve(emails));
+      adminSession.searchGroupsStub.withArgs(USERNAME).returns(Bluebird.resolve(groups));
+      
+      userSession.openStub.returns(Bluebird.resolve());
+      userSession.closeStub.returns(Bluebird.resolve());
+
+      return usersDatabase.checkUserPassword(USERNAME, PASSWORD)
+        .then((groupsAndEmails) => {
+          Assert.deepEqual(groupsAndEmails.groups, groups);
+          Assert.deepEqual(groupsAndEmails.emails, emails);
+        })
+    });
+
+    it("should fail when username/password is wrong", function() {
+      const USER_DN = `cn=${USERNAME},dc=example,dc=com`;
+
+      const adminSession = new SessionStub();
+      const userSession = new SessionStub();
+
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession);
+      sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession);
+
+      adminSession.openStub.returns(Bluebird.resolve());
+      adminSession.closeStub.returns(Bluebird.resolve());
+      adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN));
+      
+      userSession.openStub.returns(Bluebird.reject(new Error("Failed binding")));
+      userSession.closeStub.returns(Bluebird.resolve());
+
+      return usersDatabase.checkUserPassword(USERNAME, PASSWORD)
+        .then(() => Bluebird.reject(new Error("should not be here")))
+        .catch((err) => {
+          Assert(userSession.closeStub.called);
+          Assert(adminSession.closeStub.called);
+          return Bluebird.resolve();
+        })
+    });
+
+    it("should fail when admin binding fails", function() {
+      const USER_DN = `cn=${USERNAME},dc=example,dc=com`;
+
+      const adminSession = new SessionStub();
+      const userSession = new SessionStub();
+
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession);
+      sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession);
+
+      adminSession.openStub.returns(Bluebird.reject(new Error("Failed binding")));
+      adminSession.closeStub.returns(Bluebird.resolve());
+      adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN));
+
+      return usersDatabase.checkUserPassword(USERNAME, PASSWORD)
+        .then(() => Bluebird.reject(new Error("should not be here")))
+        .catch((err) => {
+          Assert(userSession.closeStub.notCalled);
+          Assert(adminSession.closeStub.called);
+          return Bluebird.resolve();
+        })
+    });
+
+    it("should fail when search for user dn fails", function() {
+      const USER_DN = `cn=${USERNAME},dc=example,dc=com`;
+
+      const adminSession = new SessionStub();
+      const userSession = new SessionStub();
+
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession);
+      sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession);
+
+      adminSession.openStub.returns(Bluebird.resolve());
+      adminSession.closeStub.returns(Bluebird.resolve());
+      adminSession.searchUserDnStub.returns(Bluebird.reject(new Error("Failed searching user dn")));
+
+      return usersDatabase.checkUserPassword(USERNAME, PASSWORD)
+        .then(() => Bluebird.reject(new Error("should not be here")))
+        .catch((err) => {
+          Assert(userSession.closeStub.notCalled);
+          Assert(adminSession.closeStub.called);
+          return Bluebird.resolve();
+        })
+    });
+
+    it("should fail when groups retrieval fails", function() {
+      const USER_DN = `cn=${USERNAME},dc=example,dc=com`;
+      const emails = ["email1", "email2"];
+      const groups = ["group1", "group2"];
+
+      const adminSession = new SessionStub();
+      const userSession = new SessionStub();
+
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession);
+      sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession);
+
+      adminSession.openStub.returns(Bluebird.resolve());
+      adminSession.closeStub.returns(Bluebird.resolve());
+      adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN));
+      adminSession.searchEmailsStub.withArgs(USERNAME)
+        .returns(Bluebird.resolve(emails));
+      adminSession.searchGroupsStub.withArgs(USERNAME)
+        .returns(Bluebird.reject(new Error("Failed retrieving groups")));
+      
+      userSession.openStub.returns(Bluebird.resolve());
+      userSession.closeStub.returns(Bluebird.resolve());
+
+      return usersDatabase.checkUserPassword(USERNAME, PASSWORD)
+        .then((groupsAndEmails) => Bluebird.reject(new Error("should not be here")))
+        .catch((err) => {
+          Assert(userSession.closeStub.called);
+          Assert(adminSession.closeStub.called);
+        })
+    });
+
+    it("should fail when emails retrieval fails", function() {
+      const USER_DN = `cn=${USERNAME},dc=example,dc=com`;
+      const emails = ["email1", "email2"];
+      const groups = ["group1", "group2"];
+
+      const adminSession = new SessionStub();
+      const userSession = new SessionStub();
+
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(adminSession);
+      sessionFactory.createStub.withArgs(USER_DN, PASSWORD).returns(userSession);
+
+      adminSession.openStub.returns(Bluebird.resolve());
+      adminSession.closeStub.returns(Bluebird.resolve());
+      adminSession.searchUserDnStub.returns(Bluebird.resolve(USER_DN));
+      adminSession.searchEmailsStub.withArgs(USERNAME)
+        .returns(Bluebird.reject(new Error("Emails retrieval failed")));
+      adminSession.searchGroupsStub.withArgs(USERNAME)
+        .returns(Bluebird.resolve(groups));
+      
+      userSession.openStub.returns(Bluebird.resolve());
+      userSession.closeStub.returns(Bluebird.resolve());
+
+      return usersDatabase.checkUserPassword(USERNAME, PASSWORD)
+        .then((groupsAndEmails) => Bluebird.reject(new Error("should not be here")))
+        .catch((err) => {
+          Assert(userSession.closeStub.called);
+          Assert(adminSession.closeStub.called);
+        })
+    });
+  });
+
+  describe("getEmails", function() {
+    it("should succefully retrieves email", () => {
+      const emails = ["email1", "email2"];
+      const session = new SessionStub();
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session);
+
+      session.openStub.returns(Bluebird.resolve());
+      session.closeStub.returns(Bluebird.resolve());
+      session.searchEmailsStub.returns(Bluebird.resolve(emails));
+
+      return usersDatabase.getEmails(USERNAME)
+        .then((foundEmails) => {
+          Assert(session.closeStub.called);
+          Assert.deepEqual(foundEmails, emails);
+        })
+    });
+
+    it("should fail when binding fails", () => {
+      const emails = ["email1", "email2"];
+      const session = new SessionStub();
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session);
+
+      session.openStub.returns(Bluebird.reject(new Error("Binding failed")));
+
+      return usersDatabase.getEmails(USERNAME)
+        .then(() => Bluebird.reject(new Error("should not be here")))
+        .catch((err) => {
+          Assert(session.closeStub.called);
+        })
+    });
+
+    it("should fail when unbinding fails", () => {
+      const emails = ["email1", "email2"];
+      const session = new SessionStub();
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session);
+
+      session.openStub.returns(Bluebird.resolve());
+      session.searchEmailsStub.returns(Bluebird.resolve(emails));
+      session.closeStub.returns(Bluebird.reject(new Error("Unbinding failed")));
+
+      return usersDatabase.getEmails(USERNAME)
+        .then(() => Bluebird.reject(new Error("should not be here")))
+        .catch((err) => {
+          Assert(session.closeStub.called);
+        })
+    });
+
+    it("should fail when search fails", () => {
+      const emails = ["email1", "email2"];
+      const session = new SessionStub();
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session);
+
+      session.openStub.returns(Bluebird.resolve());
+      session.searchEmailsStub.returns(Bluebird.reject(new Error("Search failed")));
+      session.closeStub.returns(Bluebird.resolve());
+
+      return usersDatabase.getEmails(USERNAME)
+        .then(() => Bluebird.reject(new Error("should not be here")))
+        .catch((err) => {
+          Assert(session.closeStub.called);
+        })
+    });
+  });
+
+
+  describe("getGroups", function() {
+    it("should succefully retrieves groups", () => {
+      const groups = ["group1", "group2"];
+      const session = new SessionStub();
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session);
+
+      session.openStub.returns(Bluebird.resolve());
+      session.closeStub.returns(Bluebird.resolve());
+      session.searchGroupsStub.returns(Bluebird.resolve(groups));
+
+      return usersDatabase.getGroups(USERNAME)
+        .then((foundGroups) => {
+          Assert(session.closeStub.called);
+          Assert.deepEqual(foundGroups, groups);
+        })
+    });
+
+    it("should fail when binding fails", () => {
+      const session = new SessionStub();
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session);
+
+      session.openStub.returns(Bluebird.reject(new Error("Binding failed")));
+
+      return usersDatabase.getGroups(USERNAME)
+        .then(() => Bluebird.reject(new Error("should not be here")))
+        .catch((err) => {
+          Assert(session.closeStub.called);
+        })
+    });
+
+    it("should fail when unbinding fails", () => {
+      const groups = ["group1", "group2"];
+      const session = new SessionStub();
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session);
+
+      session.openStub.returns(Bluebird.resolve());
+      session.searchGroupsStub.returns(Bluebird.resolve(groups));
+      session.closeStub.returns(Bluebird.reject(new Error("Unbinding failed")));
+
+      return usersDatabase.getGroups(USERNAME)
+        .then(() => Bluebird.reject(new Error("should not be here")))
+        .catch((err) => {
+          Assert(session.closeStub.called);
+        })
+    });
+
+    it("should fail when search fails", () => {
+      const groups = ["group1", "group2"];
+      const session = new SessionStub();
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session);
+
+      session.openStub.returns(Bluebird.resolve());
+      session.searchGroupsStub.returns(Bluebird.reject(new Error("Search failed")));
+      session.closeStub.returns(Bluebird.resolve());
+
+      return usersDatabase.getGroups(USERNAME)
+        .then(() => Bluebird.reject(new Error("should not be here")))
+        .catch((err) => {
+          Assert(session.closeStub.called);
+        })
+    });
+  });
+
+
+  describe("updatePassword", function() {
+    it("should successfully update password", () => {
+      const session = new SessionStub();
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session);
+
+      session.openStub.returns(Bluebird.resolve());
+      session.closeStub.returns(Bluebird.resolve());
+      session.modifyPasswordStub.returns(Bluebird.resolve());
+
+      return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD)
+        .then(() => {
+          Assert(session.modifyPasswordStub.calledWith(USERNAME, NEW_PASSWORD));
+          Assert(session.closeStub.called);
+        })
+    });
+
+    it("should fail when binding fails", () => {
+      const session = new SessionStub();
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session);
+
+      session.openStub.returns(Bluebird.reject(new Error("Binding failed")));
+      session.closeStub.returns(Bluebird.resolve());
+      session.modifyPasswordStub.returns(Bluebird.resolve());
+
+      return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD)
+        .then(() => Bluebird.reject(new Error("should not be here")))
+        .catch(() => {
+          Assert(session.closeStub.called);
+        })
+    });
+
+    it("should fail when update fails", () => {
+      const session = new SessionStub();
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session);
+
+      session.openStub.returns(Bluebird.resolve());
+      session.closeStub.returns(Bluebird.reject(new Error("Update failed")));
+      session.modifyPasswordStub.returns(Bluebird.resolve());
+
+      return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD)
+        .then(() => Bluebird.reject(new Error("should not be here")))
+        .catch(() => {
+          Assert(session.closeStub.called);
+        })
+    });
+
+    it("should fail when unbind fails", () => {
+      const session = new SessionStub();
+      sessionFactory.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD).returns(session);
+
+      session.openStub.returns(Bluebird.resolve());
+      session.closeStub.returns(Bluebird.resolve());
+      session.modifyPasswordStub.returns(Bluebird.reject(new Error("Unbind failed")));
+
+      return usersDatabase.updatePassword(USERNAME, NEW_PASSWORD)
+        .then(() => Bluebird.reject(new Error("should not be here")))
+        .catch(() => {
+          Assert(session.closeStub.called);
+        })
+    });
+  });
+});
\ No newline at end of file
diff --git a/server/src/lib/ldap/LdapUsersDatabase.ts b/server/src/lib/ldap/LdapUsersDatabase.ts
new file mode 100644
index 00000000..5631018c
--- /dev/null
+++ b/server/src/lib/ldap/LdapUsersDatabase.ts
@@ -0,0 +1,106 @@
+import Bluebird = require("bluebird");
+import { IUsersDatabase } from "./IUsersDatabase";
+import { ISessionFactory } from "./ISessionFactory";
+import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
+import { ISession, GroupsAndEmails } from "./ISession";
+import Exceptions = require("../Exceptions");
+
+type SessionCallback<T> = (session: ISession) => Bluebird<T>;
+
+export class LdapUsersDatabase implements IUsersDatabase {
+  private sessionFactory: ISessionFactory;
+  private configuration: LdapConfiguration;
+
+  constructor(
+    sessionFactory: ISessionFactory,
+    configuration: LdapConfiguration) {
+    this.sessionFactory = sessionFactory;
+    this.configuration = configuration;
+  }
+
+  private withSession<T>(
+    username: string,
+    password: string,
+    cb: SessionCallback<T>): Bluebird<T> {
+    const session = this.sessionFactory.create(username, password);
+    return session.open()
+      .then(() => cb(session))
+      .finally(() => session.close());
+  }
+
+  checkUserPassword(username: string, password: string): Bluebird<GroupsAndEmails> {
+    const that = this;
+    function verifyUserPassword(userDN: string) {
+      return that.withSession<void>(
+        userDN,
+        password,
+        (session) => Bluebird.resolve()
+      );
+    }
+
+    function getInfo(session: ISession) {
+        return Bluebird.join(
+          session.searchGroups(username),
+          session.searchEmails(username)
+        )
+        .spread((groups: string[], emails: string[]) => {
+          return { groups: groups, emails: emails };
+        });
+    }
+
+    return that.withSession(
+      that.configuration.user,
+      that.configuration.password,
+      (session) => {
+        return session.searchUserDn(username)
+          .then(verifyUserPassword)
+          .then(() => getInfo(session));
+      })
+      .catch((err) =>
+        Bluebird.reject(new Exceptions.LdapError(err.message)));
+  }
+
+  getEmails(username: string): Bluebird<string[]> {
+    const that = this;
+    return that.withSession(
+      that.configuration.user,
+      that.configuration.password,
+      (session) => {
+        return session.searchEmails(username);
+      }
+    )
+    .catch((err) =>
+      Bluebird.reject(new Exceptions.LdapError("Failed during email retrieval: " + err.message))
+    );
+  }
+
+  getGroups(username: string): Bluebird<string[]> {
+    const that = this;
+    return that.withSession(
+      that.configuration.user,
+      that.configuration.password,
+      (session) => {
+        return session.searchGroups(username);
+      }
+    )
+    .catch((err) =>
+      Bluebird.reject(new Exceptions.LdapError("Failed during email retrieval: " + err.message))
+    );
+  }
+
+  updatePassword(username: string, newPassword: string): Bluebird<void> {
+    const that = this;
+    return that.withSession(
+      that.configuration.user,
+      that.configuration.password,
+      (session) => {
+        return session.modifyPassword(username, newPassword);
+      }
+    )
+    .catch(function (err: Error) {
+      return Bluebird.reject(
+        new Exceptions.LdapError(
+          "Error while updating password: " + err.message));
+    });
+  }
+}
\ No newline at end of file
diff --git a/server/src/lib/ldap/PasswordUpdater.spec.ts b/server/src/lib/ldap/PasswordUpdater.spec.ts
deleted file mode 100644
index 8afb6ada..00000000
--- a/server/src/lib/ldap/PasswordUpdater.spec.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import Sinon = require("sinon");
-import BluebirdPromise = require("bluebird");
-import Assert = require("assert");
-import { PasswordUpdater } from "./PasswordUpdater";
-import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
-import { ClientFactoryStub } from "./ClientFactoryStub.spec";
-import { ClientStub } from "./ClientStub.spec";
-import { HashGenerator } from "../utils/HashGenerator";
-
-describe("ldap/PasswordUpdater", function () {
-  const USERNAME = "username";
-  const NEW_PASSWORD = "new-password";
-
-  const ADMIN_USER_DN = "cn=admin,dc=example,dc=com";
-  const ADMIN_PASSWORD = "password";
-
-  let clientFactoryStub: ClientFactoryStub;
-  let adminClientStub: ClientStub;
-  let passwordUpdater: PasswordUpdater;
-  let ldapConfig: LdapConfiguration;
-  let ssha512HashGenerator: Sinon.SinonStub;
-
-  beforeEach(function () {
-    clientFactoryStub = new ClientFactoryStub();
-    adminClientStub = new ClientStub();
-
-    ldapConfig = {
-      url: "http://ldap",
-      user: ADMIN_USER_DN,
-      password: ADMIN_PASSWORD,
-      additional_users_dn: "ou=users",
-      additional_groups_dn: "ou=groups",
-      base_dn: "dc=example,dc=com",
-      group_name_attribute: "cn",
-      groups_filter: "cn={0}",
-      mail_attribute: "mail",
-      users_filter: "cn={0}"
-    };
-
-    ssha512HashGenerator = Sinon.stub(HashGenerator, "ssha512");
-    clientFactoryStub.createStub.withArgs(ADMIN_USER_DN, ADMIN_PASSWORD)
-      .returns(adminClientStub);
-
-    passwordUpdater = new PasswordUpdater(ldapConfig, clientFactoryStub);
-  });
-
-  afterEach(function () {
-    ssha512HashGenerator.restore();
-  });
-
-  describe("success", function () {
-    it("should update the password successfully", function () {
-      ssha512HashGenerator
-        .returns("{CRYPT}$6$abcdefghijklm$AQmxaKfobGY9HSQa6aDYkAWOgPGNhGYn");
-      adminClientStub.modifyPasswordStub.withArgs(USERNAME, NEW_PASSWORD)
-        .returns(BluebirdPromise.resolve());
-      adminClientStub.openStub.returns(BluebirdPromise.resolve());
-      adminClientStub.closeStub.returns(BluebirdPromise.resolve());
-
-      return passwordUpdater.updatePassword(USERNAME, NEW_PASSWORD);
-    });
-  });
-
-  describe("failure", function () {
-    it("should fail updating password when modify operation fails",
-      function () {
-        ssha512HashGenerator
-          .returns("{CRYPT}$6$abcdefghijklm$AQmxaKfobGY9HSQa6aDYkAWOgPGNhGYn");
-        adminClientStub.modifyPasswordStub.withArgs(USERNAME, NEW_PASSWORD)
-          .rejects(new Error("Error while updating password"));
-        adminClientStub.openStub.returns(BluebirdPromise.resolve());
-        adminClientStub.closeStub.returns(BluebirdPromise.resolve());
-
-        return passwordUpdater.updatePassword(USERNAME, NEW_PASSWORD)
-          .then(function () {
-            return BluebirdPromise.reject(new Error("should not be here"));
-          })
-          .catch(function(err: Error) {
-            return BluebirdPromise.resolve();
-          });
-      });
-  });
-});
\ No newline at end of file
diff --git a/server/src/lib/ldap/PasswordUpdater.ts b/server/src/lib/ldap/PasswordUpdater.ts
deleted file mode 100644
index 03a23ba3..00000000
--- a/server/src/lib/ldap/PasswordUpdater.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import BluebirdPromise = require("bluebird");
-import exceptions = require("../Exceptions");
-import ldapjs = require("ldapjs");
-import { Client } from "./Client";
-
-import { IPasswordUpdater } from "./IPasswordUpdater";
-import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
-import { IClientFactory } from "./IClientFactory";
-
-
-export class PasswordUpdater implements IPasswordUpdater {
-  private options: LdapConfiguration;
-  private clientFactory: IClientFactory;
-
-  constructor(options: LdapConfiguration, clientFactory: IClientFactory) {
-    this.options = options;
-    this.clientFactory = clientFactory;
-  }
-
-  updatePassword(username: string, newPassword: string)
-    : BluebirdPromise<void> {
-    const adminClient = this.clientFactory.create(this.options.user,
-      this.options.password);
-
-    return adminClient.open()
-      .then(function () {
-        return adminClient.modifyPassword(username, newPassword);
-      })
-      .then(function () {
-        return adminClient.close();
-      })
-      .catch(function (err: Error) {
-        return BluebirdPromise.reject(
-          new exceptions.LdapError(
-            "Error while updating password: " + err.message));
-      });
-  }
-}
diff --git a/server/src/lib/ldap/PasswordUpdaterStub.spec.ts b/server/src/lib/ldap/PasswordUpdaterStub.spec.ts
deleted file mode 100644
index add6465c..00000000
--- a/server/src/lib/ldap/PasswordUpdaterStub.spec.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import BluebirdPromise = require("bluebird");
-import { IClient } from "./IClient";
-import { IPasswordUpdater } from "./IPasswordUpdater";
-import Sinon = require("sinon");
-
-export class PasswordUpdaterStub implements IPasswordUpdater {
-  updatePasswordStub: Sinon.SinonStub;
-
-  constructor() {
-    this.updatePasswordStub = Sinon.stub();
-  }
-
-  updatePassword(username: string, newPassword: string): BluebirdPromise<void> {
-    return this.updatePasswordStub(username, newPassword);
-  }
-}
\ No newline at end of file
diff --git a/server/src/lib/ldap/SanitizedClient.spec.ts b/server/src/lib/ldap/SafeSession.spec.ts
similarity index 92%
rename from server/src/lib/ldap/SanitizedClient.spec.ts
rename to server/src/lib/ldap/SafeSession.spec.ts
index 6b8009c1..9dedfcb7 100644
--- a/server/src/lib/ldap/SanitizedClient.spec.ts
+++ b/server/src/lib/ldap/SafeSession.spec.ts
@@ -1,17 +1,17 @@
 import BluebirdPromise = require("bluebird");
-import { ClientStub } from "./ClientStub.spec";
-import { SanitizedClient } from "./SanitizedClient";
+import { SessionStub } from "./SessionStub.spec";
+import { SafeSession } from "./SafeSession";
 
 describe("ldap/SanitizedClient", function () {
-  let client: SanitizedClient;
+  let client: SafeSession;
 
   beforeEach(function () {
-    const clientStub = new ClientStub();
+    const clientStub = new SessionStub();
     clientStub.searchUserDnStub.onCall(0).returns(BluebirdPromise.resolve());
     clientStub.searchGroupsStub.onCall(0).returns(BluebirdPromise.resolve());
     clientStub.searchEmailsStub.onCall(0).returns(BluebirdPromise.resolve());
     clientStub.modifyPasswordStub.onCall(0).returns(BluebirdPromise.resolve());
-    client = new SanitizedClient(clientStub);
+    client = new SafeSession(clientStub);
   });
 
   describe("special chars are used", function () {
diff --git a/server/src/lib/ldap/SanitizedClient.ts b/server/src/lib/ldap/SafeSession.ts
similarity index 52%
rename from server/src/lib/ldap/SanitizedClient.ts
rename to server/src/lib/ldap/SafeSession.ts
index 543557a6..4c9dc01d 100644
--- a/server/src/lib/ldap/SanitizedClient.ts
+++ b/server/src/lib/ldap/SafeSession.ts
@@ -1,30 +1,29 @@
 import BluebirdPromise = require("bluebird");
-import { IClient, GroupsAndEmails } from "./IClient";
-import { Client } from "./Client";
-import { InputsSanitizer } from "./InputsSanitizer";
+import { ISession, GroupsAndEmails } from "./ISession";
+import { Sanitizer } from "./Sanitizer";
 
 const SPECIAL_CHAR_USED_MESSAGE = "Special character used in LDAP query.";
 
 
-export class SanitizedClient implements IClient {
-  private client: IClient;
+export class SafeSession implements ISession {
+  private sesion: ISession;
 
-  constructor(client: IClient) {
-    this.client = client;
+  constructor(sesion: ISession) {
+    this.sesion = sesion;
   }
 
   open(): BluebirdPromise<void> {
-    return this.client.open();
+    return this.sesion.open();
   }
 
   close(): BluebirdPromise<void> {
-    return this.client.close();
+    return this.sesion.close();
   }
 
   searchGroups(username: string): BluebirdPromise<string[]> {
     try {
-      const sanitizedUsername = InputsSanitizer.sanitize(username);
-      return this.client.searchGroups(sanitizedUsername);
+      const sanitizedUsername = Sanitizer.sanitize(username);
+      return this.sesion.searchGroups(sanitizedUsername);
     }
     catch (e) {
       return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE));
@@ -33,8 +32,8 @@ export class SanitizedClient implements IClient {
 
   searchUserDn(username: string): BluebirdPromise<string> {
     try {
-      const sanitizedUsername = InputsSanitizer.sanitize(username);
-      return this.client.searchUserDn(sanitizedUsername);
+      const sanitizedUsername = Sanitizer.sanitize(username);
+      return this.sesion.searchUserDn(sanitizedUsername);
     }
     catch (e) {
       return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE));
@@ -43,8 +42,8 @@ export class SanitizedClient implements IClient {
 
   searchEmails(username: string): BluebirdPromise<string[]> {
     try {
-      const sanitizedUsername = InputsSanitizer.sanitize(username);
-      return this.client.searchEmails(sanitizedUsername);
+      const sanitizedUsername = Sanitizer.sanitize(username);
+      return this.sesion.searchEmails(sanitizedUsername);
     }
     catch (e) {
       return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE));
@@ -53,8 +52,8 @@ export class SanitizedClient implements IClient {
 
   modifyPassword(username: string, newPassword: string): BluebirdPromise<void> {
     try {
-      const sanitizedUsername = InputsSanitizer.sanitize(username);
-      return this.client.modifyPassword(sanitizedUsername, newPassword);
+      const sanitizedUsername = Sanitizer.sanitize(username);
+      return this.sesion.modifyPassword(sanitizedUsername, newPassword);
     }
     catch (e) {
       return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE));
diff --git a/server/src/lib/ldap/Sanitizer.spec.ts b/server/src/lib/ldap/Sanitizer.spec.ts
new file mode 100644
index 00000000..9dd33fed
--- /dev/null
+++ b/server/src/lib/ldap/Sanitizer.spec.ts
@@ -0,0 +1,25 @@
+import Assert = require("assert");
+import { Sanitizer } from "./Sanitizer";
+
+describe("ldap/InputsSanitizer", function () {
+  it("should fail when special characters are used", function () {
+    Assert.throws(() => { Sanitizer.sanitize("ab,c"); }, Error);
+    Assert.throws(() => { Sanitizer.sanitize("a\\bc"); }, Error);
+    Assert.throws(() => { Sanitizer.sanitize("a'bc"); }, Error);
+    Assert.throws(() => { Sanitizer.sanitize("a#bc"); }, Error);
+    Assert.throws(() => { Sanitizer.sanitize("a+bc"); }, Error);
+    Assert.throws(() => { Sanitizer.sanitize("a<bc"); }, Error);
+    Assert.throws(() => { Sanitizer.sanitize("a>bc"); }, Error);
+    Assert.throws(() => { Sanitizer.sanitize("a;bc"); }, Error);
+    Assert.throws(() => { Sanitizer.sanitize("a\"bc"); }, Error);
+    Assert.throws(() => { Sanitizer.sanitize("a=bc"); }, Error);
+  });
+
+  it("should return original string", function () {
+    Assert.equal(Sanitizer.sanitize("abcdef"), "abcdef");
+  });
+
+  it("should trim", function () {
+    Assert.throws(() => { Sanitizer.sanitize("    abc    "); }, Error);
+  });
+});
diff --git a/server/src/lib/ldap/InputsSanitizer.ts b/server/src/lib/ldap/Sanitizer.ts
similarity index 96%
rename from server/src/lib/ldap/InputsSanitizer.ts
rename to server/src/lib/ldap/Sanitizer.ts
index ab6152de..be74132a 100644
--- a/server/src/lib/ldap/InputsSanitizer.ts
+++ b/server/src/lib/ldap/Sanitizer.ts
@@ -11,7 +11,7 @@ function containsOneOf(s: string, characters: string[]) {
     .reduce((acc: boolean, current: boolean) => { return acc || current; }, false);
 }
 
-export class InputsSanitizer {
+export class Sanitizer {
   static sanitize(input: string): string {
     const forbiddenChars = [",", "\\", "'", "#", "+", "<", ">", ";", "\"", "="];
     if (containsOneOf(input, forbiddenChars))
diff --git a/server/src/lib/ldap/Client.spec.ts b/server/src/lib/ldap/Session.spec.ts
similarity index 73%
rename from server/src/lib/ldap/Client.spec.ts
rename to server/src/lib/ldap/Session.spec.ts
index b5f70530..164caae2 100644
--- a/server/src/lib/ldap/Client.spec.ts
+++ b/server/src/lib/ldap/Session.spec.ts
@@ -1,15 +1,15 @@
 
 import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
-import { Client } from "./Client";
-import { LdapClientFactoryStub } from "./LdapClientFactoryStub.spec";
-import { LdapClientStub } from "./LdapClientStub.spec";
+import { Session } from "./Session";
+import { ConnectorFactoryStub } from "./connector/ConnectorFactoryStub.spec";
+import { ConnectorStub } from "./connector/ConnectorStub.spec";
 
 import Sinon = require("sinon");
 import BluebirdPromise = require("bluebird");
 import Assert = require("assert");
 import Winston = require("winston");
 
-describe("ldap/Client", function () {
+describe("ldap/Session", function () {
   const USERNAME = "username";
   const ADMIN_USER_DN = "cn=admin,dc=example,dc=com";
   const ADMIN_PASSWORD = "password";
@@ -27,18 +27,15 @@ describe("ldap/Client", function () {
       user: "cn=admin,dc=example,dc=com",
       password: "password"
     };
-    const factory = new LdapClientFactoryStub();
-    const ldapClient = new LdapClientStub();
-
-    factory.createStub.returns(ldapClient);
-    ldapClient.searchAsyncStub.returns(BluebirdPromise.resolve([{
+    const connectorStub = new ConnectorStub();
+    connectorStub.searchAsyncStub.returns(BluebirdPromise.resolve([{
       cn: "group1"
     }]));
-    const client = new Client(ADMIN_USER_DN, ADMIN_PASSWORD, options, factory, Winston);
+    const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, connectorStub, Winston);
 
     return client.searchGroups("user1")
       .then(function () {
-        Assert.equal(ldapClient.searchAsyncStub.getCall(0).args[1].filter,
+        Assert.equal(connectorStub.searchAsyncStub.getCall(0).args[1].filter,
           "member=cn=user1,ou=users,dc=example,dc=com");
       });
   });
@@ -57,10 +54,7 @@ describe("ldap/Client", function () {
       user: "cn=admin,dc=example,dc=com",
       password: "password"
     };
-    const factory = new LdapClientFactoryStub();
-    const ldapClient = new LdapClientStub();
-
-    factory.createStub.returns(ldapClient);
+    const ldapClient = new ConnectorStub();
 
     // Retrieve user DN
     ldapClient.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", {
@@ -81,7 +75,7 @@ describe("ldap/Client", function () {
       cn: "group1"
     }]));
 
-    const client = new Client(ADMIN_USER_DN, ADMIN_PASSWORD, options, factory, Winston);
+    const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, ldapClient, Winston);
 
     return client.searchGroups("user1")
       .then(function (groups: string[]) {
@@ -103,13 +97,9 @@ describe("ldap/Client", function () {
       user: "cn=admin,dc=example,dc=com",
       password: "password"
     };
-    const factory = new LdapClientFactoryStub();
-    const ldapClient = new LdapClientStub();
-
-    factory.createStub.returns(ldapClient);
-
+    const connector = new ConnectorStub();
     // Retrieve user DN
-    ldapClient.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", {
+    connector.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", {
       scope: "sub",
       sizeLimit: 1,
       attributes: ["dn"],
@@ -119,7 +109,7 @@ describe("ldap/Client", function () {
     }]));
 
     // Retrieve email
-    ldapClient.searchAsyncStub.withArgs("cn=user1,ou=users,dc=example,dc=com", {
+    connector.searchAsyncStub.withArgs("cn=user1,ou=users,dc=example,dc=com", {
       scope: "base",
       sizeLimit: 1,
       attributes: ["custom_mail"],
@@ -127,7 +117,7 @@ describe("ldap/Client", function () {
       custom_mail: "user1@example.com"
     }]));
 
-    const client = new Client(ADMIN_USER_DN, ADMIN_PASSWORD, options, factory, Winston);
+    const client = new Session(ADMIN_USER_DN, ADMIN_PASSWORD, options, connector, Winston);
 
     return client.searchEmails("user1")
       .then(function (emails: string[]) {
diff --git a/server/src/lib/ldap/Client.ts b/server/src/lib/ldap/Session.ts
similarity index 87%
rename from server/src/lib/ldap/Client.ts
rename to server/src/lib/ldap/Session.ts
index 7ca215bf..e5ba1804 100644
--- a/server/src/lib/ldap/Client.ts
+++ b/server/src/lib/ldap/Session.ts
@@ -1,18 +1,17 @@
 import BluebirdPromise = require("bluebird");
 import exceptions = require("../Exceptions");
 import { EventEmitter } from "events";
-import { IClient, GroupsAndEmails } from "./IClient";
-import { ILdapClient } from "./ILdapClient";
-import { ILdapClientFactory } from "./ILdapClientFactory";
+import { ISession, GroupsAndEmails } from "./ISession";
 import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
 import { Winston } from "../../../types/Dependencies";
 import Util = require("util");
 import { HashGenerator } from "../utils/HashGenerator";
+import { IConnector } from "./connector/IConnector";
 
-export class Client implements IClient {
+export class Session implements ISession {
   private userDN: string;
   private password: string;
-  private ldapClient: ILdapClient;
+  private connector: IConnector;
   private logger: Winston;
   private options: LdapConfiguration;
 
@@ -20,12 +19,12 @@ export class Client implements IClient {
   private usersSearchBase: string;
 
   constructor(userDN: string, password: string, options: LdapConfiguration,
-    ldapClientFactory: ILdapClientFactory, logger: Winston) {
+    connector: IConnector, logger: Winston) {
     this.options = options;
     this.logger = logger;
     this.userDN = userDN;
     this.password = password;
-    this.ldapClient = ldapClientFactory.create();
+    this.connector = connector;
 
     this.groupsSearchBase = (this.options.additional_groups_dn)
       ? Util.format("%s,%s", this.options.additional_groups_dn, this.options.base_dn)
@@ -38,7 +37,7 @@ export class Client implements IClient {
 
   open(): BluebirdPromise<void> {
     this.logger.debug("LDAP: Bind user '%s'", this.userDN);
-    return this.ldapClient.bindAsync(this.userDN, this.password)
+    return this.connector.bindAsync(this.userDN, this.password)
       .error(function (err: Error) {
         return BluebirdPromise.reject(new exceptions.LdapBindError(err.message));
       });
@@ -46,7 +45,7 @@ export class Client implements IClient {
 
   close(): BluebirdPromise<void> {
     this.logger.debug("LDAP: Unbind user '%s'", this.userDN);
-    return this.ldapClient.unbindAsync()
+    return this.connector.unbindAsync()
       .error(function (err: Error) {
         return BluebirdPromise.reject(new exceptions.LdapBindError(err.message));
       });
@@ -75,7 +74,7 @@ export class Client implements IClient {
           attributes: [that.options.group_name_attribute],
           filter: groupsFilter
         };
-        return that.ldapClient.searchAsync(that.groupsSearchBase, query);
+        return that.connector.searchAsync(that.groupsSearchBase, query);
       })
       .then(function (docs: { cn: string }[]) {
         const groups = docs.map((doc: any) => { return doc.cn; });
@@ -96,7 +95,7 @@ export class Client implements IClient {
     };
 
     that.logger.debug("LDAP: searching for user dn of %s", username);
-    return that.ldapClient.searchAsync(this.usersSearchBase, query)
+    return that.connector.searchAsync(this.usersSearchBase, query)
       .then(function (users: { dn: string }[]) {
         if (users.length > 0) {
           that.logger.debug("LDAP: retrieved user dn is %s", users[0].dn);
@@ -117,7 +116,7 @@ export class Client implements IClient {
 
     return this.searchUserDn(username)
       .then(function (userDN) {
-        return that.ldapClient.searchAsync(userDN, query);
+        return that.connector.searchAsync(userDN, query);
       })
       .then(function (docs: { [mail_attribute: string]: string }[]) {
         const emails: string[] = docs
@@ -148,10 +147,10 @@ export class Client implements IClient {
           }
         };
         that.logger.debug("Password new='%s'", change.modification.userPassword);
-        return that.ldapClient.modifyAsync(res[1], change);
+        return that.connector.modifyAsync(res[1], change);
       })
       .then(function () {
-        return that.ldapClient.unbindAsync();
+        return that.connector.unbindAsync();
       });
   }
 }
diff --git a/server/src/lib/ldap/SessionFactory.ts b/server/src/lib/ldap/SessionFactory.ts
new file mode 100644
index 00000000..2a71ac8c
--- /dev/null
+++ b/server/src/lib/ldap/SessionFactory.ts
@@ -0,0 +1,37 @@
+import Ldapjs = require("ldapjs");
+import Winston = require("winston");
+
+import { IConnectorFactory } from "./connector/IConnectorFactory";
+import { ISessionFactory } from "./ISessionFactory";
+import { ISession } from "./ISession";
+import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
+import { Session } from "./Session";
+import { SafeSession } from "./SafeSession";
+
+
+export class SessionFactory implements ISessionFactory {
+  private config: LdapConfiguration;
+  private connectorFactory: IConnectorFactory;
+  private logger: typeof Winston;
+
+  constructor(ldapConfiguration: LdapConfiguration,
+    connectorFactory: IConnectorFactory,
+    logger: typeof Winston) {
+    this.config = ldapConfiguration;
+    this.connectorFactory = connectorFactory;
+    this.logger = logger;
+  }
+
+  create(userDN: string, password: string): ISession {
+    const connector = this.connectorFactory.create();
+    return new SafeSession(
+      new Session(
+        userDN,
+        password,
+        this.config,
+        connector,
+        this.logger
+      )
+    );
+  }
+}
\ No newline at end of file
diff --git a/server/src/lib/ldap/SessionFactoryStub.spec.ts b/server/src/lib/ldap/SessionFactoryStub.spec.ts
new file mode 100644
index 00000000..face3930
--- /dev/null
+++ b/server/src/lib/ldap/SessionFactoryStub.spec.ts
@@ -0,0 +1,16 @@
+import Sinon = require("sinon");
+
+import { ISession } from "./ISession";
+import { ISessionFactory } from "./ISessionFactory";
+
+export class SessionFactoryStub implements ISessionFactory {
+  createStub: Sinon.SinonStub;
+
+  constructor() {
+    this.createStub = Sinon.stub();
+  }
+
+  create(userDN: string, password: string): ISession {
+    return this.createStub(userDN, password);
+  }
+}
\ No newline at end of file
diff --git a/server/src/lib/ldap/ClientStub.spec.ts b/server/src/lib/ldap/SessionStub.spec.ts
similarity index 67%
rename from server/src/lib/ldap/ClientStub.spec.ts
rename to server/src/lib/ldap/SessionStub.spec.ts
index fabcebe7..383eb1c0 100644
--- a/server/src/lib/ldap/ClientStub.spec.ts
+++ b/server/src/lib/ldap/SessionStub.spec.ts
@@ -1,9 +1,9 @@
-
-import BluebirdPromise = require("bluebird");
-import { IClient, GroupsAndEmails } from "./IClient";
+import Bluebird = require("bluebird");
 import Sinon = require("sinon");
 
-export class ClientStub implements IClient {
+import { ISession, GroupsAndEmails } from "./ISession";
+
+export class SessionStub implements ISession {
   openStub: Sinon.SinonStub;
   closeStub: Sinon.SinonStub;
   searchUserDnStub: Sinon.SinonStub;
@@ -20,27 +20,27 @@ export class ClientStub implements IClient {
     this.modifyPasswordStub = Sinon.stub();
   }
 
-  open(): BluebirdPromise<void> {
+  open(): Bluebird<void> {
     return this.openStub();
   }
 
-  close(): BluebirdPromise<void> {
+  close(): Bluebird<void> {
     return this.closeStub();
   }
 
-  searchUserDn(username: string): BluebirdPromise<string> {
+  searchUserDn(username: string): Bluebird<string> {
     return this.searchUserDnStub(username);
   }
 
-  searchEmails(username: string): BluebirdPromise<string[]> {
+  searchEmails(username: string): Bluebird<string[]> {
     return this.searchEmailsStub(username);
   }
 
-  searchGroups(username: string): BluebirdPromise<string[]> {
+  searchGroups(username: string): Bluebird<string[]> {
     return this.searchGroupsStub(username);
   }
 
-  modifyPassword(username: string, newPassword: string): BluebirdPromise<void> {
+  modifyPassword(username: string, newPassword: string): Bluebird<void> {
     return this.modifyPasswordStub(username, newPassword);
   }
 }
\ No newline at end of file
diff --git a/server/src/lib/ldap/UsersDatabaseStub.spec.ts b/server/src/lib/ldap/UsersDatabaseStub.spec.ts
new file mode 100644
index 00000000..f92aeb6a
--- /dev/null
+++ b/server/src/lib/ldap/UsersDatabaseStub.spec.ts
@@ -0,0 +1,35 @@
+import Bluebird = require("bluebird");
+import Sinon = require("sinon");
+
+import { IUsersDatabase } from "./IUsersDatabase";
+import { GroupsAndEmails } from "./ISession";
+
+export class UsersDatabaseStub implements IUsersDatabase {
+  checkUserPasswordStub: Sinon.SinonStub;
+  getEmailsStub: Sinon.SinonStub;
+  getGroupsStub: Sinon.SinonStub;
+  updatePasswordStub: Sinon.SinonStub;
+
+  constructor() {
+    this.checkUserPasswordStub = Sinon.stub();
+    this.getEmailsStub = Sinon.stub();
+    this.getGroupsStub = Sinon.stub();
+    this.updatePasswordStub = Sinon.stub();
+  }
+
+  checkUserPassword(username: string, password: string): Bluebird<GroupsAndEmails> {
+    return this.checkUserPasswordStub(username, password);
+  }
+
+  getEmails(username: string): Bluebird<string[]> {
+    return this.getEmailsStub(username);
+  }
+
+  getGroups(username: string): Bluebird<string[]> {
+    return this.getGroupsStub(username);
+  }
+
+  updatePassword(username: string, newPassword: string): Bluebird<void> {
+    return this.updatePasswordStub(username, newPassword);
+  }
+}
\ No newline at end of file
diff --git a/server/src/lib/ldap/LdapClient.ts b/server/src/lib/ldap/connector/Connector.ts
similarity index 50%
rename from server/src/lib/ldap/LdapClient.ts
rename to server/src/lib/ldap/connector/Connector.ts
index a2709cea..866c2b5d 100644
--- a/server/src/lib/ldap/LdapClient.ts
+++ b/server/src/lib/ldap/connector/Connector.ts
@@ -1,25 +1,23 @@
 import LdapJs = require("ldapjs");
 import EventEmitter = require("events");
-import BluebirdPromise = require("bluebird");
-import { ILdapClient } from "./ILdapClient";
-import Exceptions = require("../Exceptions");
-
-declare module "ldapjs" {
-  export interface ClientAsync {
-    on(event: string, callback: (data?: any) => void): void;
-    bindAsync(username: string, password: string): BluebirdPromise<void>;
-    unbindAsync(): BluebirdPromise<void>;
-    searchAsync(base: string, query: LdapJs.SearchOptions): BluebirdPromise<EventEmitter>;
-    modifyAsync(userdn: string, change: LdapJs.Change): BluebirdPromise<void>;
-  }
-}
+import Bluebird = require("bluebird");
+import { IConnector } from "./IConnector";
+import Exceptions = require("../../Exceptions");
 
 interface SearchEntry {
   object: any;
 }
 
-export class LdapClient implements ILdapClient {
-  private client: LdapJs.ClientAsync;
+export interface ClientAsync {
+  on(event: string, callback: (data?: any) => void): void;
+  bindAsync(username: string, password: string): Bluebird<void>;
+  unbindAsync(): Bluebird<void>;
+  searchAsync(base: string, query: LdapJs.SearchOptions): Bluebird<EventEmitter>;
+  modifyAsync(userdn: string, change: LdapJs.Change): Bluebird<void>;
+}
+
+export class Connector implements IConnector {
+  private client: ClientAsync;
 
   constructor(url: string, ldapjs: typeof LdapJs) {
     const ldapClient = ldapjs.createClient({
@@ -32,23 +30,23 @@ export class LdapClient implements ILdapClient {
       clientLogger.level("trace");
     }*/
 
-    this.client = BluebirdPromise.promisifyAll(ldapClient) as any;
+    this.client = Bluebird.promisifyAll(ldapClient) as any;
   }
 
-  bindAsync(username: string, password: string): BluebirdPromise<void> {
+  bindAsync(username: string, password: string): Bluebird<void> {
     return this.client.bindAsync(username, password);
   }
 
-  unbindAsync(): BluebirdPromise<void> {
+  unbindAsync(): Bluebird<void> {
     return this.client.unbindAsync();
   }
 
-  searchAsync(base: string, query: any): BluebirdPromise<any[]> {
+  searchAsync(base: string, query: any): Bluebird<any[]> {
     const that = this;
     return this.client.searchAsync(base, query)
       .then(function (res: EventEmitter) {
         const doc: SearchEntry[] = [];
-        return new BluebirdPromise<any[]>((resolve, reject) => {
+        return new Bluebird<any[]>((resolve, reject) => {
           res.on("searchEntry", function (entry: SearchEntry) {
             doc.push(entry.object);
           });
@@ -61,11 +59,11 @@ export class LdapClient implements ILdapClient {
         });
       })
       .catch(function (err: Error) {
-        return BluebirdPromise.reject(new Exceptions.LdapSearchError(err.message));
+        return Bluebird.reject(new Exceptions.LdapSearchError(err.message));
       });
   }
 
-  modifyAsync(dn: string, changeRequest: any): BluebirdPromise<void> {
+  modifyAsync(dn: string, changeRequest: any): Bluebird<void> {
     return this.client.modifyAsync(dn, changeRequest);
   }
 }
\ No newline at end of file
diff --git a/server/src/lib/ldap/connector/ConnectorFactory.ts b/server/src/lib/ldap/connector/ConnectorFactory.ts
new file mode 100644
index 00000000..78380aa1
--- /dev/null
+++ b/server/src/lib/ldap/connector/ConnectorFactory.ts
@@ -0,0 +1,18 @@
+import { IConnector } from "./IConnector";
+import { Connector } from "./Connector";
+import { LdapConfiguration } from "../../configuration/schema/LdapConfiguration";
+import { Ldapjs } from "Dependencies";
+
+export class ConnectorFactory {
+  private configuration: LdapConfiguration;
+  private ldapjs: Ldapjs;
+
+  constructor(configuration: LdapConfiguration, ldapjs: Ldapjs) {
+    this.configuration = configuration;
+    this.ldapjs = ldapjs;
+  }
+
+  create(): IConnector {
+    return new Connector(this.configuration.url, this.ldapjs);
+  }
+}
\ No newline at end of file
diff --git a/server/src/lib/ldap/LdapClientFactoryStub.spec.ts b/server/src/lib/ldap/connector/ConnectorFactoryStub.spec.ts
similarity index 51%
rename from server/src/lib/ldap/LdapClientFactoryStub.spec.ts
rename to server/src/lib/ldap/connector/ConnectorFactoryStub.spec.ts
index 31f26149..7938a755 100644
--- a/server/src/lib/ldap/LdapClientFactoryStub.spec.ts
+++ b/server/src/lib/ldap/connector/ConnectorFactoryStub.spec.ts
@@ -1,16 +1,16 @@
 import Sinon = require("sinon");
 import BluebirdPromise = require("bluebird");
-import { ILdapClientFactory } from "./ILdapClientFactory";
-import { ILdapClient } from "./ILdapClient";
+import { IConnectorFactory } from "./IConnectorFactory";
+import { IConnector } from "./IConnector";
 
-export class LdapClientFactoryStub implements ILdapClientFactory {
+export class ConnectorFactoryStub implements IConnectorFactory {
   createStub: Sinon.SinonStub;
 
   constructor() {
     this.createStub = Sinon.stub();
   }
 
-  create(): ILdapClient {
+  create(): IConnector {
     return this.createStub();
   }
 }
\ No newline at end of file
diff --git a/server/src/lib/ldap/LdapClientStub.spec.ts b/server/src/lib/ldap/connector/ConnectorStub.spec.ts
similarity index 89%
rename from server/src/lib/ldap/LdapClientStub.spec.ts
rename to server/src/lib/ldap/connector/ConnectorStub.spec.ts
index c5ea45ff..fc902b8c 100644
--- a/server/src/lib/ldap/LdapClientStub.spec.ts
+++ b/server/src/lib/ldap/connector/ConnectorStub.spec.ts
@@ -1,8 +1,8 @@
 import Sinon = require("sinon");
 import BluebirdPromise = require("bluebird");
-import { ILdapClient } from "./ILdapClient";
+import { IConnector } from "./IConnector";
 
-export class LdapClientStub implements ILdapClient {
+export class ConnectorStub implements IConnector {
   bindAsyncStub: Sinon.SinonStub;
   unbindAsyncStub: Sinon.SinonStub;
   searchAsyncStub: Sinon.SinonStub;
diff --git a/server/src/lib/ldap/connector/IConnector.ts b/server/src/lib/ldap/connector/IConnector.ts
new file mode 100644
index 00000000..76bd35b6
--- /dev/null
+++ b/server/src/lib/ldap/connector/IConnector.ts
@@ -0,0 +1,10 @@
+
+import Bluebird = require("bluebird");
+import EventEmitter = require("events");
+
+export interface IConnector {
+  bindAsync(username: string, password: string): Bluebird<void>;
+  unbindAsync(): Bluebird<void>;
+  searchAsync(base: string, query: any): Bluebird<any[]>;
+  modifyAsync(dn: string, changeRequest: any): Bluebird<void>;
+}
\ No newline at end of file
diff --git a/server/src/lib/ldap/connector/IConnectorFactory.ts b/server/src/lib/ldap/connector/IConnectorFactory.ts
new file mode 100644
index 00000000..f9ed65ef
--- /dev/null
+++ b/server/src/lib/ldap/connector/IConnectorFactory.ts
@@ -0,0 +1,5 @@
+import { IConnector } from "./IConnector";
+
+export interface IConnectorFactory {
+  create(): IConnector;
+}
\ No newline at end of file
diff --git a/server/src/lib/routes/firstfactor/post.spec.ts b/server/src/lib/routes/firstfactor/post.spec.ts
index e3b726b9..ca014351 100644
--- a/server/src/lib/routes/firstfactor/post.spec.ts
+++ b/server/src/lib/routes/firstfactor/post.spec.ts
@@ -54,7 +54,7 @@ describe("routes/firstfactor/post", function () {
   });
 
   it("should reply with 204 if success", function () {
-    mocks.ldapAuthenticator.authenticateStub.withArgs("username", "password")
+    mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password")
       .returns(BluebirdPromise.resolve({
         emails: emails,
         groups: groups
@@ -67,14 +67,14 @@ describe("routes/firstfactor/post", function () {
   });
 
   it("should retrieve email from LDAP", function () {
-    mocks.ldapAuthenticator.authenticateStub.withArgs("username", "password")
+    mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password")
       .returns(BluebirdPromise.resolve([{ mail: ["test@example.com"] }]));
     return FirstFactorPost.default(vars)(req as any, res as any);
   });
 
   it("should set first email address as user session variable", function () {
     const emails = ["test_ok@example.com"];
-    mocks.ldapAuthenticator.authenticateStub.withArgs("username", "password")
+    mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password")
       .returns(BluebirdPromise.resolve({
         emails: emails,
         groups: groups
@@ -87,7 +87,7 @@ describe("routes/firstfactor/post", function () {
   });
 
   it("should return error message when LDAP authenticator throws", function () {
-    mocks.ldapAuthenticator.authenticateStub.withArgs("username", "password")
+    mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password")
       .returns(BluebirdPromise.reject(new exceptions.LdapBindError("Bad credentials")));
 
     return FirstFactorPost.default(vars)(req as any, res as any)
diff --git a/server/src/lib/routes/firstfactor/post.ts b/server/src/lib/routes/firstfactor/post.ts
index ecf1d85a..bc3d8053 100644
--- a/server/src/lib/routes/firstfactor/post.ts
+++ b/server/src/lib/routes/firstfactor/post.ts
@@ -5,7 +5,6 @@ import BluebirdPromise = require("bluebird");
 import express = require("express");
 import { AccessController } from "../../access_control/AccessController";
 import { Regulator } from "../../regulation/Regulator";
-import { GroupsAndEmails } from "../../ldap/IClient";
 import Endpoint = require("../../../../../shared/api");
 import ErrorReplies = require("../../ErrorReplies");
 import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
@@ -15,6 +14,7 @@ import UserMessages = require("../../../../../shared/UserMessages");
 import { MethodCalculator } from "../../authentication/MethodCalculator";
 import { ServerVariables } from "../../ServerVariables";
 import { AuthenticationSession } from "../../../../types/AuthenticationSession";
+import { GroupsAndEmails } from "../../ldap/ISession";
 
 export default function (vars: ServerVariables) {
   return function (req: express.Request, res: express.Response)
@@ -34,7 +34,7 @@ export default function (vars: ServerVariables) {
       })
       .then(function () {
         vars.logger.info(req, "No regulation applied.");
-        return vars.ldapAuthenticator.authenticate(username, password);
+        return vars.usersDatabase.checkUserPassword(username, password);
       })
       .then(function (groupsAndEmails: GroupsAndEmails) {
         vars.logger.info(req,
diff --git a/server/src/lib/routes/password-reset/form/post.spec.ts b/server/src/lib/routes/password-reset/form/post.spec.ts
index 04efe177..667ce35a 100644
--- a/server/src/lib/routes/password-reset/form/post.spec.ts
+++ b/server/src/lib/routes/password-reset/form/post.spec.ts
@@ -1,6 +1,5 @@
 
 import PasswordResetFormPost = require("./post");
-import { PasswordUpdater } from "../../../ldap/PasswordUpdater";
 import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler";
 import { AuthenticationSession } from "../../../../../types/AuthenticationSession";
 import { UserDataStore } from "../../../storage/UserDataStore";
@@ -69,7 +68,7 @@ describe("routes/password-reset/form/post", function () {
       req.body = {};
       req.body.password = "new-password";
 
-      mocks.ldapPasswordUpdater.updatePasswordStub.returns(BluebirdPromise.resolve());
+      mocks.usersDatabase.updatePasswordStub.returns(BluebirdPromise.resolve());
 
       authSession.identity_check = {
         userid: "user",
@@ -104,7 +103,7 @@ describe("routes/password-reset/form/post", function () {
       req.body = {};
       req.body.password = "new-password";
 
-      mocks.ldapPasswordUpdater.updatePasswordStub
+      mocks.usersDatabase.updatePasswordStub
         .returns(BluebirdPromise.reject("Internal error with LDAP"));
 
       authSession.identity_check = {
diff --git a/server/src/lib/routes/password-reset/form/post.ts b/server/src/lib/routes/password-reset/form/post.ts
index ed7bba68..fccd7471 100644
--- a/server/src/lib/routes/password-reset/form/post.ts
+++ b/server/src/lib/routes/password-reset/form/post.ts
@@ -34,7 +34,7 @@ export default function (vars: ServerVariables) {
       resolve();
     })
       .then(function () {
-        return vars.ldapPasswordUpdater.updatePassword(authSession.identity_check.userid, newPassword);
+        return vars.usersDatabase.updatePassword(authSession.identity_check.userid, newPassword);
       })
       .then(function () {
         vars.logger.info(req, "Password reset for user '%s'",
diff --git a/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts b/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts
index 34d699ca..ac6a4175 100644
--- a/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts
+++ b/server/src/lib/routes/password-reset/identity/PasswordResetHandler.spec.ts
@@ -1,7 +1,6 @@
 
 import PasswordResetHandler
   from "./PasswordResetHandler";
-import PasswordUpdater = require("../../../ldap/PasswordUpdater");
 import { UserDataStore } from "../../../storage/UserDataStore";
 import Sinon = require("sinon");
 import winston = require("winston");
@@ -60,7 +59,7 @@ describe("routes/password-reset/identity/PasswordResetHandler", function () {
     it("should fail when no userid is provided", function () {
       req.query.userid = undefined;
       const handler = new PasswordResetHandler(vars.logger,
-        vars.ldapEmailsRetriever);
+        vars.usersDatabase);
       return handler.preValidationInit(req as any)
         .then(function () {
           return BluebirdPromise.reject("It should fail");
@@ -71,9 +70,9 @@ describe("routes/password-reset/identity/PasswordResetHandler", function () {
     });
 
     it("should fail if ldap fail", function () {
-      mocks.ldapEmailsRetriever.retrieveStub
+      mocks.usersDatabase.getEmailsStub
         .returns(BluebirdPromise.reject("Internal error"));
-      new PasswordResetHandler(vars.logger, vars.ldapEmailsRetriever)
+      new PasswordResetHandler(vars.logger, vars.usersDatabase)
         .preValidationInit(req as any)
         .then(function () {
           return BluebirdPromise.reject(new Error("should not be here"));
@@ -84,9 +83,9 @@ describe("routes/password-reset/identity/PasswordResetHandler", function () {
     });
 
     it("should returns identity when ldap replies", function () {
-      mocks.ldapEmailsRetriever.retrieveStub
+      mocks.usersDatabase.getEmailsStub
         .returns(BluebirdPromise.resolve(["test@example.com"]));
-      return new PasswordResetHandler(vars.logger, vars.ldapEmailsRetriever)
+      return new PasswordResetHandler(vars.logger, vars.usersDatabase)
         .preValidationInit(req as any);
     });
   });
diff --git a/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts b/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts
index 5cce78ea..ab8ef4f3 100644
--- a/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts
+++ b/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts
@@ -8,17 +8,17 @@ import { IdentityValidable } from "../../../IdentityValidable";
 import { PRE_VALIDATION_TEMPLATE } from "../../../IdentityCheckPreValidationTemplate";
 import Constants = require("../constants");
 import { IRequestLogger } from "../../../logging/IRequestLogger";
-import { IEmailsRetriever } from "../../../ldap/IEmailsRetriever";
+import { IUsersDatabase } from "../../../ldap/IUsersDatabase";
 
 export const TEMPLATE_NAME = "password-reset-form";
 
 export default class PasswordResetHandler implements IdentityValidable {
   private logger: IRequestLogger;
-  private emailsRetriever: IEmailsRetriever;
+  private usersDatabase: IUsersDatabase;
 
-  constructor(logger: IRequestLogger, emailsRetriever: IEmailsRetriever) {
+  constructor(logger: IRequestLogger, usersDatabase: IUsersDatabase) {
     this.logger = logger;
-    this.emailsRetriever = emailsRetriever;
+    this.usersDatabase = usersDatabase;
   }
 
   challenge(): string {
@@ -36,7 +36,7 @@ export default class PasswordResetHandler implements IdentityValidable {
           return BluebirdPromise.reject(
             new exceptions.AccessDeniedError("No user id provided"));
 
-        return that.emailsRetriever.retrieve(userid);
+        return that.usersDatabase.getEmails(userid);
       })
       .then(function (emails: string[]) {
         if (!emails && emails.length <= 0) throw new Error("No email found");
diff --git a/server/src/lib/routes/verify/get.spec.ts b/server/src/lib/routes/verify/get.spec.ts
index cb8cb586..effa1309 100644
--- a/server/src/lib/routes/verify/get.spec.ts
+++ b/server/src/lib/routes/verify/get.spec.ts
@@ -1,12 +1,13 @@
 
 import Assert = require("assert");
+import BluebirdPromise = require("bluebird");
+import Express = require("express");
+import Sinon = require("sinon");
+import winston = require("winston");
+
 import VerifyGet = require("./get");
 import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
 import { AuthenticationSession } from "../../../../types/AuthenticationSession";
-import Sinon = require("sinon");
-import winston = require("winston");
-import BluebirdPromise = require("bluebird");
-import express = require("express");
 import ExpressMock = require("../../stubs/express.spec");
 import { ServerVariables } from "../../ServerVariables";
 import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../ServerVariablesMockBuilder.spec";
@@ -44,7 +45,7 @@ describe("routes/verify/get", function () {
       authSession.second_factor = true;
       authSession.userid = "myuser";
       authSession.groups = ["mygroup", "othergroup"];
-      return VerifyGet.default(vars)(req as express.Request, res as any)
+      return VerifyGet.default(vars)(req as Express.Request, res as any)
         .then(function () {
           Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "myuser");
           Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup");
@@ -53,7 +54,7 @@ describe("routes/verify/get", function () {
     });
 
     function test_session(_authSession: AuthenticationSession, status_code: number) {
-      return VerifyGet.default(vars)(req as express.Request, res as any)
+      return VerifyGet.default(vars)(req as Express.Request, res as any)
         .then(function () {
           Assert.equal(status_code, res.status.getCall(0).args[0]);
         });
@@ -156,7 +157,7 @@ describe("routes/verify/get", function () {
         mocks.accessController.isAccessAllowedMock.returns(true);
         authSession.first_factor = true;
         authSession.userid = "user1";
-        return VerifyGet.default(vars)(req as express.Request, res as any)
+        return VerifyGet.default(vars)(req as Express.Request, res as any)
           .then(function () {
             Assert(res.status.calledWith(204));
             Assert(res.send.calledOnce);
@@ -166,7 +167,7 @@ describe("routes/verify/get", function () {
       it("should be rejected with 401 when first factor is not validated", function () {
         mocks.accessController.isAccessAllowedMock.returns(true);
         authSession.first_factor = false;
-        return VerifyGet.default(vars)(req as express.Request, res as any)
+        return VerifyGet.default(vars)(req as Express.Request, res as any)
           .then(function () {
             Assert(res.status.calledWith(401));
           });
@@ -184,7 +185,7 @@ describe("routes/verify/get", function () {
         authSession.userid = "myuser";
         authSession.groups = ["mygroup", "othergroup"];
         authSession.last_activity_datetime = currentTime;
-        return VerifyGet.default(vars)(req as express.Request, res as any)
+        return VerifyGet.default(vars)(req as Express.Request, res as any)
           .then(function () {
             return AuthenticationSessionHandler.get(req as any, vars.logger);
           })
@@ -203,7 +204,7 @@ describe("routes/verify/get", function () {
         authSession.userid = "myuser";
         authSession.groups = ["mygroup", "othergroup"];
         authSession.last_activity_datetime = currentTime;
-        return VerifyGet.default(vars)(req as express.Request, res as any)
+        return VerifyGet.default(vars)(req as Express.Request, res as any)
           .then(function () {
             return AuthenticationSessionHandler.get(req as any, vars.logger);
           })
@@ -220,11 +221,11 @@ describe("routes/verify/get", function () {
     it("should return error code 401", function() {
       mocks.accessController.isAccessAllowedMock.returns(true);
       mocks.config.authentication_methods.default_method = "single_factor";
-      mocks.ldapAuthenticator.authenticateStub.rejects(new Error(
+      mocks.usersDatabase.checkUserPasswordStub.rejects(new Error(
         "Invalid credentials"));
       req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA==";
 
-      return VerifyGet.default(vars)(req as express.Request, res as any)
+      return VerifyGet.default(vars)(req as Express.Request, res as any)
         .then(function () {
           Assert(res.status.calledWithExactly(401));
         });
@@ -234,12 +235,12 @@ describe("routes/verify/get", function () {
       const REDIRECT_URL = "http://redirection_url.com";
       mocks.accessController.isAccessAllowedMock.returns(true);
       mocks.config.authentication_methods.default_method = "single_factor";
-      mocks.ldapAuthenticator.authenticateStub.rejects(new Error(
+      mocks.usersDatabase.checkUserPasswordStub.rejects(new Error(
         "Invalid credentials"));
       req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA==";
       req.query["rd"] = REDIRECT_URL;
 
-      return VerifyGet.default(vars)(req as express.Request, res as any)
+      return VerifyGet.default(vars)(req as Express.Request, res as any)
         .then(function () {
           Assert(res.redirect.calledWithExactly(REDIRECT_URL));
         });
@@ -250,12 +251,12 @@ describe("routes/verify/get", function () {
     it("should authenticate correctly", function () {
       mocks.accessController.isAccessAllowedMock.returns(true);
       mocks.config.authentication_methods.default_method = "single_factor";
-      mocks.ldapAuthenticator.authenticateStub.returns({
+      mocks.usersDatabase.checkUserPasswordStub.returns({
         groups: ["mygroup", "othergroup"],
       });
       req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA==";
 
-      return VerifyGet.default(vars)(req as express.Request, res as any)
+      return VerifyGet.default(vars)(req as Express.Request, res as any)
         .then(function () {
           Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "john");
           Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup");
@@ -269,12 +270,12 @@ describe("routes/verify/get", function () {
       mocks.config.authentication_methods.per_subdomain_methods = {
         "secret.example.com": "two_factor"
       };
-      mocks.ldapAuthenticator.authenticateStub.resolves({
+      mocks.usersDatabase.checkUserPasswordStub.resolves({
         groups: ["mygroup", "othergroup"],
       });
       req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA==";
 
-      return VerifyGet.default(vars)(req as express.Request, res as any)
+      return VerifyGet.default(vars)(req as Express.Request, res as any)
         .then(function () {
           Assert(res.status.calledWithExactly(401));
         });
@@ -283,12 +284,12 @@ describe("routes/verify/get", function () {
     it("should fail when base64 token is not valid", function () {
       mocks.accessController.isAccessAllowedMock.returns(true);
       mocks.config.authentication_methods.default_method = "single_factor";
-      mocks.ldapAuthenticator.authenticateStub.resolves({
+      mocks.usersDatabase.checkUserPasswordStub.resolves({
         groups: ["mygroup", "othergroup"],
       });
       req.headers["proxy-authorization"] = "Basic i_m*not_a_base64*token";
 
-      return VerifyGet.default(vars)(req as express.Request, res as any)
+      return VerifyGet.default(vars)(req as Express.Request, res as any)
         .then(function () {
           Assert(res.status.calledWithExactly(401));
         });
@@ -297,12 +298,12 @@ describe("routes/verify/get", function () {
     it("should fail when base64 token has not format user:psswd", function () {
       mocks.accessController.isAccessAllowedMock.returns(true);
       mocks.config.authentication_methods.default_method = "single_factor";
-      mocks.ldapAuthenticator.authenticateStub.resolves({
+      mocks.usersDatabase.checkUserPasswordStub.resolves({
         groups: ["mygroup", "othergroup"],
       });
       req.headers["proxy-authorization"] = "Basic am9objpwYXNzOmJhZA==";
 
-      return VerifyGet.default(vars)(req as express.Request, res as any)
+      return VerifyGet.default(vars)(req as Express.Request, res as any)
         .then(function () {
           Assert(res.status.calledWithExactly(401));
         });
@@ -311,11 +312,11 @@ describe("routes/verify/get", function () {
     it("should fail when bad user password is provided", function () {
       mocks.accessController.isAccessAllowedMock.returns(true);
       mocks.config.authentication_methods.default_method = "single_factor";
-      mocks.ldapAuthenticator.authenticateStub.rejects(new Error(
+      mocks.usersDatabase.checkUserPasswordStub.rejects(new Error(
         "Invalid credentials"));
       req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA==";
 
-      return VerifyGet.default(vars)(req as express.Request, res as any)
+      return VerifyGet.default(vars)(req as Express.Request, res as any)
         .then(function () {
           Assert(res.status.calledWithExactly(401));
         });
@@ -324,12 +325,12 @@ describe("routes/verify/get", function () {
     it("should fail when resource is restricted", function () {
       mocks.accessController.isAccessAllowedMock.returns(false);
       mocks.config.authentication_methods.default_method = "single_factor";
-      mocks.ldapAuthenticator.authenticateStub.resolves({
+      mocks.usersDatabase.checkUserPasswordStub.resolves({
         groups: ["mygroup", "othergroup"],
       });
       req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA==";
 
-      return VerifyGet.default(vars)(req as express.Request, res as any)
+      return VerifyGet.default(vars)(req as Express.Request, res as any)
         .then(function () {
           Assert(res.status.calledWithExactly(401));
         });
diff --git a/server/src/lib/routes/verify/get_basic_auth.ts b/server/src/lib/routes/verify/get_basic_auth.ts
index e92160b5..e489bb88 100644
--- a/server/src/lib/routes/verify/get_basic_auth.ts
+++ b/server/src/lib/routes/verify/get_basic_auth.ts
@@ -12,60 +12,51 @@ export default function (req: Express.Request, res: Express.Response,
   vars: ServerVariables, authorizationHeader: string)
   : BluebirdPromise<{ username: string, groups: string[] }> {
   let username: string;
-  let groups: string[];
   let domain: string;
   let originalUri: string;
 
-  return new BluebirdPromise<[string, string]>(function (resolve, reject) {
-    const originalUrl = ObjectPath.get<Express.Request, string>(req, "headers.x-original-url");
-    domain = DomainExtractor.fromUrl(originalUrl);
-    originalUri =
-      ObjectPath.get<Express.Request, string>(req, "headers.x-original-uri");
-    const authenticationMethod =
-      MethodCalculator.compute(vars.config.authentication_methods, domain);
+  return BluebirdPromise.resolve()
+    .then(() => {
+      const originalUrl = ObjectPath.get<Express.Request, string>(req, "headers.x-original-url");
+      domain = DomainExtractor.fromUrl(originalUrl);
+      originalUri =
+        ObjectPath.get<Express.Request, string>(req, "headers.x-original-uri");
+      const authenticationMethod =
+        MethodCalculator.compute(vars.config.authentication_methods, domain);
 
-    if (authenticationMethod != "single_factor") {
-      reject(new Error("This domain is not protected with single factor. " +
-        "You cannot log in with basic authentication."));
-      return;
-    }
+      if (authenticationMethod != "single_factor") {
+        return BluebirdPromise.reject(new Error("This domain is not protected with single factor. " +
+          "You cannot log in with basic authentication."));
+      }
 
-    const base64Re = new RegExp("^Basic ((?:[A-Za-z0-9+/]{4})*" +
-      "(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?)$");
-    const isTokenValidBase64 = base64Re.test(authorizationHeader);
+      const base64Re = new RegExp("^Basic ((?:[A-Za-z0-9+/]{4})*" +
+        "(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?)$");
+      const isTokenValidBase64 = base64Re.test(authorizationHeader);
 
-    if (!isTokenValidBase64) {
-      reject(new Error("No valid base64 token found in the header"));
-      return;
-    }
+      if (!isTokenValidBase64) {
+        return BluebirdPromise.reject(new Error("No valid base64 token found in the header"));
+      }
 
-    const tokenMatches = authorizationHeader.match(base64Re);
-    const base64Token = tokenMatches[1];
-    const decodedToken = Buffer.from(base64Token, "base64").toString();
-    const splittedToken = decodedToken.split(":");
+      const tokenMatches = authorizationHeader.match(base64Re);
+      const base64Token = tokenMatches[1];
+      const decodedToken = Buffer.from(base64Token, "base64").toString();
+      const splittedToken = decodedToken.split(":");
 
-    if (splittedToken.length != 2) {
-      reject(new Error(
-        "The authorization token is invalid. Expecting 'userid:password'"));
-      return;
-    }
+      if (splittedToken.length != 2) {
+        return BluebirdPromise.reject(new Error(
+          "The authorization token is invalid. Expecting 'userid:password'"));
+      }
 
-    username = splittedToken[0];
-    const password = splittedToken[1];
-    resolve([username, password]);
-  })
-    .then(function ([userid, password]) {
-      return vars.ldapAuthenticator.authenticate(userid, password);
+      username = splittedToken[0];
+      const password = splittedToken[1];
+      return vars.usersDatabase.checkUserPassword(username, password);
     })
     .then(function (groupsAndEmails) {
-      groups = groupsAndEmails.groups;
-      return AccessControl(req, vars, domain, originalUri, username, groups);
-    })
-    .then(function () {
-      return BluebirdPromise.resolve({
-        username: username,
-        groups: groups
-      });
+      return AccessControl(req, vars, domain, originalUri, username, groupsAndEmails.groups)
+        .then(() => BluebirdPromise.resolve({
+          username: username,
+          groups: groupsAndEmails.groups
+        }));
     })
     .catch(function (err: Error) {
       return BluebirdPromise.reject(
diff --git a/server/src/lib/web_server/RestApi.ts b/server/src/lib/web_server/RestApi.ts
index 204003c8..56408c75 100644
--- a/server/src/lib/web_server/RestApi.ts
+++ b/server/src/lib/web_server/RestApi.ts
@@ -104,7 +104,7 @@ function setupResetPassword(app: Express.Application, vars: ServerVariables) {
   IdentityCheckMiddleware.register(app,
     Endpoints.RESET_PASSWORD_IDENTITY_START_GET,
     Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET,
-    new ResetPasswordIdentityHandler(vars.logger, vars.ldapEmailsRetriever),
+    new ResetPasswordIdentityHandler(vars.logger, vars.usersDatabase),
     vars);
 
   app.get(Endpoints.RESET_PASSWORD_REQUEST_GET,