mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
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)
This commit is contained in:
parent
e50b798edc
commit
6438a5e48f
6
package-lock.json
generated
6
package-lock.json
generated
|
@ -958,9 +958,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bluebird": {
|
"bluebird": {
|
||||||
"version": "3.5.1",
|
"version": "3.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz",
|
||||||
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
|
"integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw="
|
||||||
},
|
},
|
||||||
"bn.js": {
|
"bn.js": {
|
||||||
"version": "4.11.8",
|
"version": "4.11.8",
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
"@types/helmet": "0.0.37",
|
"@types/helmet": "0.0.37",
|
||||||
"@types/jquery": "^3.3.1",
|
"@types/jquery": "^3.3.1",
|
||||||
"@types/jsdom": "^11.0.4",
|
"@types/jsdom": "^11.0.4",
|
||||||
"@types/ldapjs": "^1.0.2",
|
"@types/ldapjs": "^1.0.3",
|
||||||
"@types/mocha": "^5.0.0",
|
"@types/mocha": "^5.0.0",
|
||||||
"@types/mockdate": "^2.0.0",
|
"@types/mockdate": "^2.0.0",
|
||||||
"@types/mongodb": "^3.0.9",
|
"@types/mongodb": "^3.0.9",
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import { IRequestLogger } from "./logging/IRequestLogger";
|
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 { ITotpHandler } from "./authentication/totp/ITotpHandler";
|
||||||
import { IU2fHandler } from "./authentication/u2f/IU2fHandler";
|
import { IU2fHandler } from "./authentication/u2f/IU2fHandler";
|
||||||
import { IUserDataStore } from "./storage/IUserDataStore";
|
import { IUserDataStore } from "./storage/IUserDataStore";
|
||||||
|
@ -9,12 +6,11 @@ import { INotifier } from "./notifiers/INotifier";
|
||||||
import { IRegulator } from "./regulation/IRegulator";
|
import { IRegulator } from "./regulation/IRegulator";
|
||||||
import { Configuration } from "./configuration/schema/Configuration";
|
import { Configuration } from "./configuration/schema/Configuration";
|
||||||
import { IAccessController } from "./access_control/IAccessController";
|
import { IAccessController } from "./access_control/IAccessController";
|
||||||
|
import { IUsersDatabase } from "./ldap/IUsersDatabase";
|
||||||
|
|
||||||
export interface ServerVariables {
|
export interface ServerVariables {
|
||||||
logger: IRequestLogger;
|
logger: IRequestLogger;
|
||||||
ldapAuthenticator: IAuthenticator;
|
usersDatabase: IUsersDatabase;
|
||||||
ldapPasswordUpdater: IPasswordUpdater;
|
|
||||||
ldapEmailsRetriever: IEmailsRetriever;
|
|
||||||
totpHandler: ITotpHandler;
|
totpHandler: ITotpHandler;
|
||||||
u2f: IU2fHandler;
|
u2f: IU2fHandler;
|
||||||
userDataStore: IUserDataStore;
|
userDataStore: IUserDataStore;
|
||||||
|
|
|
@ -7,19 +7,12 @@ import Nodemailer = require("nodemailer");
|
||||||
import { IRequestLogger } from "./logging/IRequestLogger";
|
import { IRequestLogger } from "./logging/IRequestLogger";
|
||||||
import { RequestLogger } from "./logging/RequestLogger";
|
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 { TotpHandler } from "./authentication/totp/TotpHandler";
|
||||||
import { ITotpHandler } from "./authentication/totp/ITotpHandler";
|
import { ITotpHandler } from "./authentication/totp/ITotpHandler";
|
||||||
import { NotifierFactory } from "./notifiers/NotifierFactory";
|
import { NotifierFactory } from "./notifiers/NotifierFactory";
|
||||||
import { MailSenderBuilder } from "./notifiers/MailSenderBuilder";
|
import { MailSenderBuilder } from "./notifiers/MailSenderBuilder";
|
||||||
|
import { LdapUsersDatabase } from "./ldap/LdapUsersDatabase";
|
||||||
|
import { ConnectorFactory } from "./ldap/connector/ConnectorFactory";
|
||||||
|
|
||||||
import { IUserDataStore } from "./storage/IUserDataStore";
|
import { IUserDataStore } from "./storage/IUserDataStore";
|
||||||
import { UserDataStore } from "./storage/UserDataStore";
|
import { UserDataStore } from "./storage/UserDataStore";
|
||||||
|
@ -39,6 +32,7 @@ import { ServerVariables } from "./ServerVariables";
|
||||||
import { MethodCalculator } from "./authentication/MethodCalculator";
|
import { MethodCalculator } from "./authentication/MethodCalculator";
|
||||||
import { MongoClient } from "./connectors/mongo/MongoClient";
|
import { MongoClient } from "./connectors/mongo/MongoClient";
|
||||||
import { IGlobalLogger } from "./logging/IGlobalLogger";
|
import { IGlobalLogger } from "./logging/IGlobalLogger";
|
||||||
|
import { SessionFactory } from "./ldap/SessionFactory";
|
||||||
|
|
||||||
class UserDataStoreFactory {
|
class UserDataStoreFactory {
|
||||||
static create(config: Configuration.Configuration, globalLogger: IGlobalLogger): BluebirdPromise<UserDataStore> {
|
static create(config: Configuration.Configuration, globalLogger: IGlobalLogger): BluebirdPromise<UserDataStore> {
|
||||||
|
@ -72,13 +66,14 @@ export class ServerVariablesInitializer {
|
||||||
|
|
||||||
const mailSenderBuilder = new MailSenderBuilder(Nodemailer);
|
const mailSenderBuilder = new MailSenderBuilder(Nodemailer);
|
||||||
const notifier = NotifierFactory.build(config.notifier, mailSenderBuilder);
|
const notifier = NotifierFactory.build(config.notifier, mailSenderBuilder);
|
||||||
const ldapClientFactory = new LdapClientFactory(config.ldap, deps.ldapjs);
|
const ldapUsersDatabase = new LdapUsersDatabase(
|
||||||
const clientFactory = new ClientFactory(config.ldap, ldapClientFactory,
|
new SessionFactory(
|
||||||
deps.winston);
|
config.ldap,
|
||||||
|
new ConnectorFactory(config.ldap, deps.ldapjs),
|
||||||
const ldapAuthenticator = new Authenticator(config.ldap, clientFactory);
|
deps.winston
|
||||||
const ldapPasswordUpdater = new PasswordUpdater(config.ldap, clientFactory);
|
),
|
||||||
const ldapEmailsRetriever = new EmailsRetriever(config.ldap, clientFactory);
|
config.ldap
|
||||||
|
);
|
||||||
const accessController = new AccessController(config.access_control, deps.winston);
|
const accessController = new AccessController(config.access_control, deps.winston);
|
||||||
const totpHandler = new TotpHandler(deps.speakeasy);
|
const totpHandler = new TotpHandler(deps.speakeasy);
|
||||||
|
|
||||||
|
@ -90,9 +85,7 @@ export class ServerVariablesInitializer {
|
||||||
const variables: ServerVariables = {
|
const variables: ServerVariables = {
|
||||||
accessController: accessController,
|
accessController: accessController,
|
||||||
config: config,
|
config: config,
|
||||||
ldapAuthenticator: ldapAuthenticator,
|
usersDatabase: ldapUsersDatabase,
|
||||||
ldapPasswordUpdater: ldapPasswordUpdater,
|
|
||||||
ldapEmailsRetriever: ldapEmailsRetriever,
|
|
||||||
logger: requestLogger,
|
logger: requestLogger,
|
||||||
notifier: notifier,
|
notifier: notifier,
|
||||||
regulator: regulator,
|
regulator: regulator,
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { ServerVariables } from "./ServerVariables";
|
import { ServerVariables } from "./ServerVariables";
|
||||||
|
|
||||||
import { Configuration } from "./configuration/schema/Configuration";
|
import { Configuration } from "./configuration/schema/Configuration";
|
||||||
import { AuthenticatorStub } from "./ldap/AuthenticatorStub.spec";
|
import { UsersDatabaseStub } from "./ldap/UsersDatabaseStub.spec";
|
||||||
import { EmailsRetrieverStub } from "./ldap/EmailsRetrieverStub.spec";
|
|
||||||
import { PasswordUpdaterStub } from "./ldap/PasswordUpdaterStub.spec";
|
|
||||||
import { AccessControllerStub } from "./access_control/AccessControllerStub.spec";
|
import { AccessControllerStub } from "./access_control/AccessControllerStub.spec";
|
||||||
import { RequestLoggerStub } from "./logging/RequestLoggerStub.spec";
|
import { RequestLoggerStub } from "./logging/RequestLoggerStub.spec";
|
||||||
import { NotifierStub } from "./notifiers/NotifierStub.spec";
|
import { NotifierStub } from "./notifiers/NotifierStub.spec";
|
||||||
|
@ -15,9 +13,7 @@ import { U2fHandlerStub } from "./authentication/u2f/U2fHandlerStub.spec";
|
||||||
export interface ServerVariablesMock {
|
export interface ServerVariablesMock {
|
||||||
accessController: AccessControllerStub;
|
accessController: AccessControllerStub;
|
||||||
config: Configuration;
|
config: Configuration;
|
||||||
ldapAuthenticator: AuthenticatorStub;
|
usersDatabase: UsersDatabaseStub;
|
||||||
ldapEmailsRetriever: EmailsRetrieverStub;
|
|
||||||
ldapPasswordUpdater: PasswordUpdaterStub;
|
|
||||||
logger: RequestLoggerStub;
|
logger: RequestLoggerStub;
|
||||||
notifier: NotifierStub;
|
notifier: NotifierStub;
|
||||||
regulator: RegulatorStub;
|
regulator: RegulatorStub;
|
||||||
|
@ -64,9 +60,7 @@ export class ServerVariablesMockBuilder {
|
||||||
},
|
},
|
||||||
storage: {}
|
storage: {}
|
||||||
},
|
},
|
||||||
ldapAuthenticator: new AuthenticatorStub(),
|
usersDatabase: new UsersDatabaseStub(),
|
||||||
ldapEmailsRetriever: new EmailsRetrieverStub(),
|
|
||||||
ldapPasswordUpdater: new PasswordUpdaterStub(),
|
|
||||||
logger: new RequestLoggerStub(enableLogging),
|
logger: new RequestLoggerStub(enableLogging),
|
||||||
notifier: new NotifierStub(),
|
notifier: new NotifierStub(),
|
||||||
regulator: new RegulatorStub(),
|
regulator: new RegulatorStub(),
|
||||||
|
@ -77,9 +71,7 @@ export class ServerVariablesMockBuilder {
|
||||||
const vars: ServerVariables = {
|
const vars: ServerVariables = {
|
||||||
accessController: mocks.accessController,
|
accessController: mocks.accessController,
|
||||||
config: mocks.config,
|
config: mocks.config,
|
||||||
ldapAuthenticator: mocks.ldapAuthenticator,
|
usersDatabase: mocks.usersDatabase,
|
||||||
ldapEmailsRetriever: mocks.ldapEmailsRetriever,
|
|
||||||
ldapPasswordUpdater: mocks.ldapPasswordUpdater,
|
|
||||||
logger: mocks.logger,
|
logger: mocks.logger,
|
||||||
notifier: mocks.notifier,
|
notifier: mocks.notifier,
|
||||||
regulator: mocks.regulator,
|
regulator: mocks.regulator,
|
||||||
|
|
|
@ -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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
import BluebirdPromise = require("bluebird");
|
|
||||||
import { GroupsAndEmails } from "./IClient";
|
|
||||||
|
|
||||||
export interface IAuthenticator {
|
|
||||||
authenticate(username: string, password: string): BluebirdPromise<GroupsAndEmails>;
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
|
|
||||||
import { IClient } from "./IClient";
|
|
||||||
|
|
||||||
export interface IClientFactory {
|
|
||||||
create(userDN: string, password: string): IClient;
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
import BluebirdPromise = require("bluebird");
|
|
||||||
import { IClient } from "./IClient";
|
|
||||||
|
|
||||||
export interface IEmailsRetriever {
|
|
||||||
retrieve(username: string, client?: IClient): BluebirdPromise<string[]>;
|
|
||||||
}
|
|
|
@ -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>;
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
|
|
||||||
import { ILdapClient } from "./ILdapClient";
|
|
||||||
|
|
||||||
export interface ILdapClientFactory {
|
|
||||||
create(): ILdapClient;
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
import BluebirdPromise = require("bluebird");
|
|
||||||
|
|
||||||
export interface IPasswordUpdater {
|
|
||||||
updatePassword(username: string, newPassword: string): BluebirdPromise<void>;
|
|
||||||
}
|
|
|
@ -6,9 +6,10 @@ export interface GroupsAndEmails {
|
||||||
emails: string[];
|
emails: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IClient {
|
export interface ISession {
|
||||||
open(): BluebirdPromise<void>;
|
open(): BluebirdPromise<void>;
|
||||||
close(): BluebirdPromise<void>;
|
close(): BluebirdPromise<void>;
|
||||||
|
|
||||||
searchUserDn(username: string): BluebirdPromise<string>;
|
searchUserDn(username: string): BluebirdPromise<string>;
|
||||||
searchEmails(username: string): BluebirdPromise<string[]>;
|
searchEmails(username: string): BluebirdPromise<string[]>;
|
||||||
searchGroups(username: string): BluebirdPromise<string[]>;
|
searchGroups(username: string): BluebirdPromise<string[]>;
|
6
server/src/lib/ldap/ISessionFactory.ts
Normal file
6
server/src/lib/ldap/ISessionFactory.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
import { ISession } from "./ISession";
|
||||||
|
|
||||||
|
export interface ISessionFactory {
|
||||||
|
create(userDN: string, password: string): ISession;
|
||||||
|
}
|
9
server/src/lib/ldap/IUsersDatabase.ts
Normal file
9
server/src/lib/ldap/IUsersDatabase.ts
Normal file
|
@ -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>;
|
||||||
|
}
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
386
server/src/lib/ldap/LdapUsersDatabase.spec.ts
Normal file
386
server/src/lib/ldap/LdapUsersDatabase.spec.ts
Normal file
|
@ -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);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
106
server/src/lib/ldap/LdapUsersDatabase.ts
Normal file
106
server/src/lib/ldap/LdapUsersDatabase.ts
Normal file
|
@ -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));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +1,17 @@
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import { ClientStub } from "./ClientStub.spec";
|
import { SessionStub } from "./SessionStub.spec";
|
||||||
import { SanitizedClient } from "./SanitizedClient";
|
import { SafeSession } from "./SafeSession";
|
||||||
|
|
||||||
describe("ldap/SanitizedClient", function () {
|
describe("ldap/SanitizedClient", function () {
|
||||||
let client: SanitizedClient;
|
let client: SafeSession;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
const clientStub = new ClientStub();
|
const clientStub = new SessionStub();
|
||||||
clientStub.searchUserDnStub.onCall(0).returns(BluebirdPromise.resolve());
|
clientStub.searchUserDnStub.onCall(0).returns(BluebirdPromise.resolve());
|
||||||
clientStub.searchGroupsStub.onCall(0).returns(BluebirdPromise.resolve());
|
clientStub.searchGroupsStub.onCall(0).returns(BluebirdPromise.resolve());
|
||||||
clientStub.searchEmailsStub.onCall(0).returns(BluebirdPromise.resolve());
|
clientStub.searchEmailsStub.onCall(0).returns(BluebirdPromise.resolve());
|
||||||
clientStub.modifyPasswordStub.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 () {
|
describe("special chars are used", function () {
|
|
@ -1,30 +1,29 @@
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import { IClient, GroupsAndEmails } from "./IClient";
|
import { ISession, GroupsAndEmails } from "./ISession";
|
||||||
import { Client } from "./Client";
|
import { Sanitizer } from "./Sanitizer";
|
||||||
import { InputsSanitizer } from "./InputsSanitizer";
|
|
||||||
|
|
||||||
const SPECIAL_CHAR_USED_MESSAGE = "Special character used in LDAP query.";
|
const SPECIAL_CHAR_USED_MESSAGE = "Special character used in LDAP query.";
|
||||||
|
|
||||||
|
|
||||||
export class SanitizedClient implements IClient {
|
export class SafeSession implements ISession {
|
||||||
private client: IClient;
|
private sesion: ISession;
|
||||||
|
|
||||||
constructor(client: IClient) {
|
constructor(sesion: ISession) {
|
||||||
this.client = client;
|
this.sesion = sesion;
|
||||||
}
|
}
|
||||||
|
|
||||||
open(): BluebirdPromise<void> {
|
open(): BluebirdPromise<void> {
|
||||||
return this.client.open();
|
return this.sesion.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
close(): BluebirdPromise<void> {
|
close(): BluebirdPromise<void> {
|
||||||
return this.client.close();
|
return this.sesion.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
searchGroups(username: string): BluebirdPromise<string[]> {
|
searchGroups(username: string): BluebirdPromise<string[]> {
|
||||||
try {
|
try {
|
||||||
const sanitizedUsername = InputsSanitizer.sanitize(username);
|
const sanitizedUsername = Sanitizer.sanitize(username);
|
||||||
return this.client.searchGroups(sanitizedUsername);
|
return this.sesion.searchGroups(sanitizedUsername);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE));
|
return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE));
|
||||||
|
@ -33,8 +32,8 @@ export class SanitizedClient implements IClient {
|
||||||
|
|
||||||
searchUserDn(username: string): BluebirdPromise<string> {
|
searchUserDn(username: string): BluebirdPromise<string> {
|
||||||
try {
|
try {
|
||||||
const sanitizedUsername = InputsSanitizer.sanitize(username);
|
const sanitizedUsername = Sanitizer.sanitize(username);
|
||||||
return this.client.searchUserDn(sanitizedUsername);
|
return this.sesion.searchUserDn(sanitizedUsername);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE));
|
return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE));
|
||||||
|
@ -43,8 +42,8 @@ export class SanitizedClient implements IClient {
|
||||||
|
|
||||||
searchEmails(username: string): BluebirdPromise<string[]> {
|
searchEmails(username: string): BluebirdPromise<string[]> {
|
||||||
try {
|
try {
|
||||||
const sanitizedUsername = InputsSanitizer.sanitize(username);
|
const sanitizedUsername = Sanitizer.sanitize(username);
|
||||||
return this.client.searchEmails(sanitizedUsername);
|
return this.sesion.searchEmails(sanitizedUsername);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE));
|
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> {
|
modifyPassword(username: string, newPassword: string): BluebirdPromise<void> {
|
||||||
try {
|
try {
|
||||||
const sanitizedUsername = InputsSanitizer.sanitize(username);
|
const sanitizedUsername = Sanitizer.sanitize(username);
|
||||||
return this.client.modifyPassword(sanitizedUsername, newPassword);
|
return this.sesion.modifyPassword(sanitizedUsername, newPassword);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE));
|
return BluebirdPromise.reject(new Error(SPECIAL_CHAR_USED_MESSAGE));
|
25
server/src/lib/ldap/Sanitizer.spec.ts
Normal file
25
server/src/lib/ldap/Sanitizer.spec.ts
Normal file
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -11,7 +11,7 @@ function containsOneOf(s: string, characters: string[]) {
|
||||||
.reduce((acc: boolean, current: boolean) => { return acc || current; }, false);
|
.reduce((acc: boolean, current: boolean) => { return acc || current; }, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InputsSanitizer {
|
export class Sanitizer {
|
||||||
static sanitize(input: string): string {
|
static sanitize(input: string): string {
|
||||||
const forbiddenChars = [",", "\\", "'", "#", "+", "<", ">", ";", "\"", "="];
|
const forbiddenChars = [",", "\\", "'", "#", "+", "<", ">", ";", "\"", "="];
|
||||||
if (containsOneOf(input, forbiddenChars))
|
if (containsOneOf(input, forbiddenChars))
|
|
@ -1,15 +1,15 @@
|
||||||
|
|
||||||
import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
|
import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
|
||||||
import { Client } from "./Client";
|
import { Session } from "./Session";
|
||||||
import { LdapClientFactoryStub } from "./LdapClientFactoryStub.spec";
|
import { ConnectorFactoryStub } from "./connector/ConnectorFactoryStub.spec";
|
||||||
import { LdapClientStub } from "./LdapClientStub.spec";
|
import { ConnectorStub } from "./connector/ConnectorStub.spec";
|
||||||
|
|
||||||
import Sinon = require("sinon");
|
import Sinon = require("sinon");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
import Winston = require("winston");
|
import Winston = require("winston");
|
||||||
|
|
||||||
describe("ldap/Client", function () {
|
describe("ldap/Session", function () {
|
||||||
const USERNAME = "username";
|
const USERNAME = "username";
|
||||||
const ADMIN_USER_DN = "cn=admin,dc=example,dc=com";
|
const ADMIN_USER_DN = "cn=admin,dc=example,dc=com";
|
||||||
const ADMIN_PASSWORD = "password";
|
const ADMIN_PASSWORD = "password";
|
||||||
|
@ -27,18 +27,15 @@ describe("ldap/Client", function () {
|
||||||
user: "cn=admin,dc=example,dc=com",
|
user: "cn=admin,dc=example,dc=com",
|
||||||
password: "password"
|
password: "password"
|
||||||
};
|
};
|
||||||
const factory = new LdapClientFactoryStub();
|
const connectorStub = new ConnectorStub();
|
||||||
const ldapClient = new LdapClientStub();
|
connectorStub.searchAsyncStub.returns(BluebirdPromise.resolve([{
|
||||||
|
|
||||||
factory.createStub.returns(ldapClient);
|
|
||||||
ldapClient.searchAsyncStub.returns(BluebirdPromise.resolve([{
|
|
||||||
cn: "group1"
|
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")
|
return client.searchGroups("user1")
|
||||||
.then(function () {
|
.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");
|
"member=cn=user1,ou=users,dc=example,dc=com");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -57,10 +54,7 @@ describe("ldap/Client", function () {
|
||||||
user: "cn=admin,dc=example,dc=com",
|
user: "cn=admin,dc=example,dc=com",
|
||||||
password: "password"
|
password: "password"
|
||||||
};
|
};
|
||||||
const factory = new LdapClientFactoryStub();
|
const ldapClient = new ConnectorStub();
|
||||||
const ldapClient = new LdapClientStub();
|
|
||||||
|
|
||||||
factory.createStub.returns(ldapClient);
|
|
||||||
|
|
||||||
// Retrieve user DN
|
// Retrieve user DN
|
||||||
ldapClient.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", {
|
ldapClient.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", {
|
||||||
|
@ -81,7 +75,7 @@ describe("ldap/Client", function () {
|
||||||
cn: "group1"
|
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")
|
return client.searchGroups("user1")
|
||||||
.then(function (groups: string[]) {
|
.then(function (groups: string[]) {
|
||||||
|
@ -103,13 +97,9 @@ describe("ldap/Client", function () {
|
||||||
user: "cn=admin,dc=example,dc=com",
|
user: "cn=admin,dc=example,dc=com",
|
||||||
password: "password"
|
password: "password"
|
||||||
};
|
};
|
||||||
const factory = new LdapClientFactoryStub();
|
const connector = new ConnectorStub();
|
||||||
const ldapClient = new LdapClientStub();
|
|
||||||
|
|
||||||
factory.createStub.returns(ldapClient);
|
|
||||||
|
|
||||||
// Retrieve user DN
|
// Retrieve user DN
|
||||||
ldapClient.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", {
|
connector.searchAsyncStub.withArgs("ou=users,dc=example,dc=com", {
|
||||||
scope: "sub",
|
scope: "sub",
|
||||||
sizeLimit: 1,
|
sizeLimit: 1,
|
||||||
attributes: ["dn"],
|
attributes: ["dn"],
|
||||||
|
@ -119,7 +109,7 @@ describe("ldap/Client", function () {
|
||||||
}]));
|
}]));
|
||||||
|
|
||||||
// Retrieve email
|
// 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",
|
scope: "base",
|
||||||
sizeLimit: 1,
|
sizeLimit: 1,
|
||||||
attributes: ["custom_mail"],
|
attributes: ["custom_mail"],
|
||||||
|
@ -127,7 +117,7 @@ describe("ldap/Client", function () {
|
||||||
custom_mail: "user1@example.com"
|
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")
|
return client.searchEmails("user1")
|
||||||
.then(function (emails: string[]) {
|
.then(function (emails: string[]) {
|
|
@ -1,18 +1,17 @@
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import exceptions = require("../Exceptions");
|
import exceptions = require("../Exceptions");
|
||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
import { IClient, GroupsAndEmails } from "./IClient";
|
import { ISession, GroupsAndEmails } from "./ISession";
|
||||||
import { ILdapClient } from "./ILdapClient";
|
|
||||||
import { ILdapClientFactory } from "./ILdapClientFactory";
|
|
||||||
import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
|
import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
|
||||||
import { Winston } from "../../../types/Dependencies";
|
import { Winston } from "../../../types/Dependencies";
|
||||||
import Util = require("util");
|
import Util = require("util");
|
||||||
import { HashGenerator } from "../utils/HashGenerator";
|
import { HashGenerator } from "../utils/HashGenerator";
|
||||||
|
import { IConnector } from "./connector/IConnector";
|
||||||
|
|
||||||
export class Client implements IClient {
|
export class Session implements ISession {
|
||||||
private userDN: string;
|
private userDN: string;
|
||||||
private password: string;
|
private password: string;
|
||||||
private ldapClient: ILdapClient;
|
private connector: IConnector;
|
||||||
private logger: Winston;
|
private logger: Winston;
|
||||||
private options: LdapConfiguration;
|
private options: LdapConfiguration;
|
||||||
|
|
||||||
|
@ -20,12 +19,12 @@ export class Client implements IClient {
|
||||||
private usersSearchBase: string;
|
private usersSearchBase: string;
|
||||||
|
|
||||||
constructor(userDN: string, password: string, options: LdapConfiguration,
|
constructor(userDN: string, password: string, options: LdapConfiguration,
|
||||||
ldapClientFactory: ILdapClientFactory, logger: Winston) {
|
connector: IConnector, logger: Winston) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.userDN = userDN;
|
this.userDN = userDN;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
this.ldapClient = ldapClientFactory.create();
|
this.connector = connector;
|
||||||
|
|
||||||
this.groupsSearchBase = (this.options.additional_groups_dn)
|
this.groupsSearchBase = (this.options.additional_groups_dn)
|
||||||
? Util.format("%s,%s", this.options.additional_groups_dn, this.options.base_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> {
|
open(): BluebirdPromise<void> {
|
||||||
this.logger.debug("LDAP: Bind user '%s'", this.userDN);
|
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) {
|
.error(function (err: Error) {
|
||||||
return BluebirdPromise.reject(new exceptions.LdapBindError(err.message));
|
return BluebirdPromise.reject(new exceptions.LdapBindError(err.message));
|
||||||
});
|
});
|
||||||
|
@ -46,7 +45,7 @@ export class Client implements IClient {
|
||||||
|
|
||||||
close(): BluebirdPromise<void> {
|
close(): BluebirdPromise<void> {
|
||||||
this.logger.debug("LDAP: Unbind user '%s'", this.userDN);
|
this.logger.debug("LDAP: Unbind user '%s'", this.userDN);
|
||||||
return this.ldapClient.unbindAsync()
|
return this.connector.unbindAsync()
|
||||||
.error(function (err: Error) {
|
.error(function (err: Error) {
|
||||||
return BluebirdPromise.reject(new exceptions.LdapBindError(err.message));
|
return BluebirdPromise.reject(new exceptions.LdapBindError(err.message));
|
||||||
});
|
});
|
||||||
|
@ -75,7 +74,7 @@ export class Client implements IClient {
|
||||||
attributes: [that.options.group_name_attribute],
|
attributes: [that.options.group_name_attribute],
|
||||||
filter: groupsFilter
|
filter: groupsFilter
|
||||||
};
|
};
|
||||||
return that.ldapClient.searchAsync(that.groupsSearchBase, query);
|
return that.connector.searchAsync(that.groupsSearchBase, query);
|
||||||
})
|
})
|
||||||
.then(function (docs: { cn: string }[]) {
|
.then(function (docs: { cn: string }[]) {
|
||||||
const groups = docs.map((doc: any) => { return doc.cn; });
|
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);
|
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 }[]) {
|
.then(function (users: { dn: string }[]) {
|
||||||
if (users.length > 0) {
|
if (users.length > 0) {
|
||||||
that.logger.debug("LDAP: retrieved user dn is %s", users[0].dn);
|
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)
|
return this.searchUserDn(username)
|
||||||
.then(function (userDN) {
|
.then(function (userDN) {
|
||||||
return that.ldapClient.searchAsync(userDN, query);
|
return that.connector.searchAsync(userDN, query);
|
||||||
})
|
})
|
||||||
.then(function (docs: { [mail_attribute: string]: string }[]) {
|
.then(function (docs: { [mail_attribute: string]: string }[]) {
|
||||||
const emails: string[] = docs
|
const emails: string[] = docs
|
||||||
|
@ -148,10 +147,10 @@ export class Client implements IClient {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
that.logger.debug("Password new='%s'", change.modification.userPassword);
|
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 () {
|
.then(function () {
|
||||||
return that.ldapClient.unbindAsync();
|
return that.connector.unbindAsync();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
37
server/src/lib/ldap/SessionFactory.ts
Normal file
37
server/src/lib/ldap/SessionFactory.ts
Normal file
|
@ -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
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
16
server/src/lib/ldap/SessionFactoryStub.spec.ts
Normal file
16
server/src/lib/ldap/SessionFactoryStub.spec.ts
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
|
import Bluebird = require("bluebird");
|
||||||
import BluebirdPromise = require("bluebird");
|
|
||||||
import { IClient, GroupsAndEmails } from "./IClient";
|
|
||||||
import Sinon = require("sinon");
|
import Sinon = require("sinon");
|
||||||
|
|
||||||
export class ClientStub implements IClient {
|
import { ISession, GroupsAndEmails } from "./ISession";
|
||||||
|
|
||||||
|
export class SessionStub implements ISession {
|
||||||
openStub: Sinon.SinonStub;
|
openStub: Sinon.SinonStub;
|
||||||
closeStub: Sinon.SinonStub;
|
closeStub: Sinon.SinonStub;
|
||||||
searchUserDnStub: Sinon.SinonStub;
|
searchUserDnStub: Sinon.SinonStub;
|
||||||
|
@ -20,27 +20,27 @@ export class ClientStub implements IClient {
|
||||||
this.modifyPasswordStub = Sinon.stub();
|
this.modifyPasswordStub = Sinon.stub();
|
||||||
}
|
}
|
||||||
|
|
||||||
open(): BluebirdPromise<void> {
|
open(): Bluebird<void> {
|
||||||
return this.openStub();
|
return this.openStub();
|
||||||
}
|
}
|
||||||
|
|
||||||
close(): BluebirdPromise<void> {
|
close(): Bluebird<void> {
|
||||||
return this.closeStub();
|
return this.closeStub();
|
||||||
}
|
}
|
||||||
|
|
||||||
searchUserDn(username: string): BluebirdPromise<string> {
|
searchUserDn(username: string): Bluebird<string> {
|
||||||
return this.searchUserDnStub(username);
|
return this.searchUserDnStub(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
searchEmails(username: string): BluebirdPromise<string[]> {
|
searchEmails(username: string): Bluebird<string[]> {
|
||||||
return this.searchEmailsStub(username);
|
return this.searchEmailsStub(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
searchGroups(username: string): BluebirdPromise<string[]> {
|
searchGroups(username: string): Bluebird<string[]> {
|
||||||
return this.searchGroupsStub(username);
|
return this.searchGroupsStub(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
modifyPassword(username: string, newPassword: string): BluebirdPromise<void> {
|
modifyPassword(username: string, newPassword: string): Bluebird<void> {
|
||||||
return this.modifyPasswordStub(username, newPassword);
|
return this.modifyPasswordStub(username, newPassword);
|
||||||
}
|
}
|
||||||
}
|
}
|
35
server/src/lib/ldap/UsersDatabaseStub.spec.ts
Normal file
35
server/src/lib/ldap/UsersDatabaseStub.spec.ts
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +1,23 @@
|
||||||
import LdapJs = require("ldapjs");
|
import LdapJs = require("ldapjs");
|
||||||
import EventEmitter = require("events");
|
import EventEmitter = require("events");
|
||||||
import BluebirdPromise = require("bluebird");
|
import Bluebird = require("bluebird");
|
||||||
import { ILdapClient } from "./ILdapClient";
|
import { IConnector } from "./IConnector";
|
||||||
import Exceptions = require("../Exceptions");
|
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>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SearchEntry {
|
interface SearchEntry {
|
||||||
object: any;
|
object: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LdapClient implements ILdapClient {
|
export interface ClientAsync {
|
||||||
private client: LdapJs.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) {
|
constructor(url: string, ldapjs: typeof LdapJs) {
|
||||||
const ldapClient = ldapjs.createClient({
|
const ldapClient = ldapjs.createClient({
|
||||||
|
@ -32,23 +30,23 @@ export class LdapClient implements ILdapClient {
|
||||||
clientLogger.level("trace");
|
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);
|
return this.client.bindAsync(username, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
unbindAsync(): BluebirdPromise<void> {
|
unbindAsync(): Bluebird<void> {
|
||||||
return this.client.unbindAsync();
|
return this.client.unbindAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
searchAsync(base: string, query: any): BluebirdPromise<any[]> {
|
searchAsync(base: string, query: any): Bluebird<any[]> {
|
||||||
const that = this;
|
const that = this;
|
||||||
return this.client.searchAsync(base, query)
|
return this.client.searchAsync(base, query)
|
||||||
.then(function (res: EventEmitter) {
|
.then(function (res: EventEmitter) {
|
||||||
const doc: SearchEntry[] = [];
|
const doc: SearchEntry[] = [];
|
||||||
return new BluebirdPromise<any[]>((resolve, reject) => {
|
return new Bluebird<any[]>((resolve, reject) => {
|
||||||
res.on("searchEntry", function (entry: SearchEntry) {
|
res.on("searchEntry", function (entry: SearchEntry) {
|
||||||
doc.push(entry.object);
|
doc.push(entry.object);
|
||||||
});
|
});
|
||||||
|
@ -61,11 +59,11 @@ export class LdapClient implements ILdapClient {
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(function (err: Error) {
|
.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);
|
return this.client.modifyAsync(dn, changeRequest);
|
||||||
}
|
}
|
||||||
}
|
}
|
18
server/src/lib/ldap/connector/ConnectorFactory.ts
Normal file
18
server/src/lib/ldap/connector/ConnectorFactory.ts
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,16 @@
|
||||||
import Sinon = require("sinon");
|
import Sinon = require("sinon");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import { ILdapClientFactory } from "./ILdapClientFactory";
|
import { IConnectorFactory } from "./IConnectorFactory";
|
||||||
import { ILdapClient } from "./ILdapClient";
|
import { IConnector } from "./IConnector";
|
||||||
|
|
||||||
export class LdapClientFactoryStub implements ILdapClientFactory {
|
export class ConnectorFactoryStub implements IConnectorFactory {
|
||||||
createStub: Sinon.SinonStub;
|
createStub: Sinon.SinonStub;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.createStub = Sinon.stub();
|
this.createStub = Sinon.stub();
|
||||||
}
|
}
|
||||||
|
|
||||||
create(): ILdapClient {
|
create(): IConnector {
|
||||||
return this.createStub();
|
return this.createStub();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
import Sinon = require("sinon");
|
import Sinon = require("sinon");
|
||||||
import BluebirdPromise = require("bluebird");
|
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;
|
bindAsyncStub: Sinon.SinonStub;
|
||||||
unbindAsyncStub: Sinon.SinonStub;
|
unbindAsyncStub: Sinon.SinonStub;
|
||||||
searchAsyncStub: Sinon.SinonStub;
|
searchAsyncStub: Sinon.SinonStub;
|
10
server/src/lib/ldap/connector/IConnector.ts
Normal file
10
server/src/lib/ldap/connector/IConnector.ts
Normal file
|
@ -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>;
|
||||||
|
}
|
5
server/src/lib/ldap/connector/IConnectorFactory.ts
Normal file
5
server/src/lib/ldap/connector/IConnectorFactory.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { IConnector } from "./IConnector";
|
||||||
|
|
||||||
|
export interface IConnectorFactory {
|
||||||
|
create(): IConnector;
|
||||||
|
}
|
|
@ -54,7 +54,7 @@ describe("routes/firstfactor/post", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should reply with 204 if success", function () {
|
it("should reply with 204 if success", function () {
|
||||||
mocks.ldapAuthenticator.authenticateStub.withArgs("username", "password")
|
mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password")
|
||||||
.returns(BluebirdPromise.resolve({
|
.returns(BluebirdPromise.resolve({
|
||||||
emails: emails,
|
emails: emails,
|
||||||
groups: groups
|
groups: groups
|
||||||
|
@ -67,14 +67,14 @@ describe("routes/firstfactor/post", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should retrieve email from LDAP", 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"] }]));
|
.returns(BluebirdPromise.resolve([{ mail: ["test@example.com"] }]));
|
||||||
return FirstFactorPost.default(vars)(req as any, res as any);
|
return FirstFactorPost.default(vars)(req as any, res as any);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set first email address as user session variable", function () {
|
it("should set first email address as user session variable", function () {
|
||||||
const emails = ["test_ok@example.com"];
|
const emails = ["test_ok@example.com"];
|
||||||
mocks.ldapAuthenticator.authenticateStub.withArgs("username", "password")
|
mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password")
|
||||||
.returns(BluebirdPromise.resolve({
|
.returns(BluebirdPromise.resolve({
|
||||||
emails: emails,
|
emails: emails,
|
||||||
groups: groups
|
groups: groups
|
||||||
|
@ -87,7 +87,7 @@ describe("routes/firstfactor/post", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return error message when LDAP authenticator throws", 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")));
|
.returns(BluebirdPromise.reject(new exceptions.LdapBindError("Bad credentials")));
|
||||||
|
|
||||||
return FirstFactorPost.default(vars)(req as any, res as any)
|
return FirstFactorPost.default(vars)(req as any, res as any)
|
||||||
|
|
|
@ -5,7 +5,6 @@ import BluebirdPromise = require("bluebird");
|
||||||
import express = require("express");
|
import express = require("express");
|
||||||
import { AccessController } from "../../access_control/AccessController";
|
import { AccessController } from "../../access_control/AccessController";
|
||||||
import { Regulator } from "../../regulation/Regulator";
|
import { Regulator } from "../../regulation/Regulator";
|
||||||
import { GroupsAndEmails } from "../../ldap/IClient";
|
|
||||||
import Endpoint = require("../../../../../shared/api");
|
import Endpoint = require("../../../../../shared/api");
|
||||||
import ErrorReplies = require("../../ErrorReplies");
|
import ErrorReplies = require("../../ErrorReplies");
|
||||||
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
||||||
|
@ -15,6 +14,7 @@ import UserMessages = require("../../../../../shared/UserMessages");
|
||||||
import { MethodCalculator } from "../../authentication/MethodCalculator";
|
import { MethodCalculator } from "../../authentication/MethodCalculator";
|
||||||
import { ServerVariables } from "../../ServerVariables";
|
import { ServerVariables } from "../../ServerVariables";
|
||||||
import { AuthenticationSession } from "../../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../../types/AuthenticationSession";
|
||||||
|
import { GroupsAndEmails } from "../../ldap/ISession";
|
||||||
|
|
||||||
export default function (vars: ServerVariables) {
|
export default function (vars: ServerVariables) {
|
||||||
return function (req: express.Request, res: express.Response)
|
return function (req: express.Request, res: express.Response)
|
||||||
|
@ -34,7 +34,7 @@ export default function (vars: ServerVariables) {
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
vars.logger.info(req, "No regulation applied.");
|
vars.logger.info(req, "No regulation applied.");
|
||||||
return vars.ldapAuthenticator.authenticate(username, password);
|
return vars.usersDatabase.checkUserPassword(username, password);
|
||||||
})
|
})
|
||||||
.then(function (groupsAndEmails: GroupsAndEmails) {
|
.then(function (groupsAndEmails: GroupsAndEmails) {
|
||||||
vars.logger.info(req,
|
vars.logger.info(req,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
|
|
||||||
import PasswordResetFormPost = require("./post");
|
import PasswordResetFormPost = require("./post");
|
||||||
import { PasswordUpdater } from "../../../ldap/PasswordUpdater";
|
|
||||||
import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler";
|
import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession } from "../../../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../../../types/AuthenticationSession";
|
||||||
import { UserDataStore } from "../../../storage/UserDataStore";
|
import { UserDataStore } from "../../../storage/UserDataStore";
|
||||||
|
@ -69,7 +68,7 @@ describe("routes/password-reset/form/post", function () {
|
||||||
req.body = {};
|
req.body = {};
|
||||||
req.body.password = "new-password";
|
req.body.password = "new-password";
|
||||||
|
|
||||||
mocks.ldapPasswordUpdater.updatePasswordStub.returns(BluebirdPromise.resolve());
|
mocks.usersDatabase.updatePasswordStub.returns(BluebirdPromise.resolve());
|
||||||
|
|
||||||
authSession.identity_check = {
|
authSession.identity_check = {
|
||||||
userid: "user",
|
userid: "user",
|
||||||
|
@ -104,7 +103,7 @@ describe("routes/password-reset/form/post", function () {
|
||||||
req.body = {};
|
req.body = {};
|
||||||
req.body.password = "new-password";
|
req.body.password = "new-password";
|
||||||
|
|
||||||
mocks.ldapPasswordUpdater.updatePasswordStub
|
mocks.usersDatabase.updatePasswordStub
|
||||||
.returns(BluebirdPromise.reject("Internal error with LDAP"));
|
.returns(BluebirdPromise.reject("Internal error with LDAP"));
|
||||||
|
|
||||||
authSession.identity_check = {
|
authSession.identity_check = {
|
||||||
|
|
|
@ -34,7 +34,7 @@ export default function (vars: ServerVariables) {
|
||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return vars.ldapPasswordUpdater.updatePassword(authSession.identity_check.userid, newPassword);
|
return vars.usersDatabase.updatePassword(authSession.identity_check.userid, newPassword);
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
vars.logger.info(req, "Password reset for user '%s'",
|
vars.logger.info(req, "Password reset for user '%s'",
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
|
|
||||||
import PasswordResetHandler
|
import PasswordResetHandler
|
||||||
from "./PasswordResetHandler";
|
from "./PasswordResetHandler";
|
||||||
import PasswordUpdater = require("../../../ldap/PasswordUpdater");
|
|
||||||
import { UserDataStore } from "../../../storage/UserDataStore";
|
import { UserDataStore } from "../../../storage/UserDataStore";
|
||||||
import Sinon = require("sinon");
|
import Sinon = require("sinon");
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
|
@ -60,7 +59,7 @@ describe("routes/password-reset/identity/PasswordResetHandler", function () {
|
||||||
it("should fail when no userid is provided", function () {
|
it("should fail when no userid is provided", function () {
|
||||||
req.query.userid = undefined;
|
req.query.userid = undefined;
|
||||||
const handler = new PasswordResetHandler(vars.logger,
|
const handler = new PasswordResetHandler(vars.logger,
|
||||||
vars.ldapEmailsRetriever);
|
vars.usersDatabase);
|
||||||
return handler.preValidationInit(req as any)
|
return handler.preValidationInit(req as any)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return BluebirdPromise.reject("It should fail");
|
return BluebirdPromise.reject("It should fail");
|
||||||
|
@ -71,9 +70,9 @@ describe("routes/password-reset/identity/PasswordResetHandler", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should fail if ldap fail", function () {
|
it("should fail if ldap fail", function () {
|
||||||
mocks.ldapEmailsRetriever.retrieveStub
|
mocks.usersDatabase.getEmailsStub
|
||||||
.returns(BluebirdPromise.reject("Internal error"));
|
.returns(BluebirdPromise.reject("Internal error"));
|
||||||
new PasswordResetHandler(vars.logger, vars.ldapEmailsRetriever)
|
new PasswordResetHandler(vars.logger, vars.usersDatabase)
|
||||||
.preValidationInit(req as any)
|
.preValidationInit(req as any)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return BluebirdPromise.reject(new Error("should not be here"));
|
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 () {
|
it("should returns identity when ldap replies", function () {
|
||||||
mocks.ldapEmailsRetriever.retrieveStub
|
mocks.usersDatabase.getEmailsStub
|
||||||
.returns(BluebirdPromise.resolve(["test@example.com"]));
|
.returns(BluebirdPromise.resolve(["test@example.com"]));
|
||||||
return new PasswordResetHandler(vars.logger, vars.ldapEmailsRetriever)
|
return new PasswordResetHandler(vars.logger, vars.usersDatabase)
|
||||||
.preValidationInit(req as any);
|
.preValidationInit(req as any);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,17 +8,17 @@ import { IdentityValidable } from "../../../IdentityValidable";
|
||||||
import { PRE_VALIDATION_TEMPLATE } from "../../../IdentityCheckPreValidationTemplate";
|
import { PRE_VALIDATION_TEMPLATE } from "../../../IdentityCheckPreValidationTemplate";
|
||||||
import Constants = require("../constants");
|
import Constants = require("../constants");
|
||||||
import { IRequestLogger } from "../../../logging/IRequestLogger";
|
import { IRequestLogger } from "../../../logging/IRequestLogger";
|
||||||
import { IEmailsRetriever } from "../../../ldap/IEmailsRetriever";
|
import { IUsersDatabase } from "../../../ldap/IUsersDatabase";
|
||||||
|
|
||||||
export const TEMPLATE_NAME = "password-reset-form";
|
export const TEMPLATE_NAME = "password-reset-form";
|
||||||
|
|
||||||
export default class PasswordResetHandler implements IdentityValidable {
|
export default class PasswordResetHandler implements IdentityValidable {
|
||||||
private logger: IRequestLogger;
|
private logger: IRequestLogger;
|
||||||
private emailsRetriever: IEmailsRetriever;
|
private usersDatabase: IUsersDatabase;
|
||||||
|
|
||||||
constructor(logger: IRequestLogger, emailsRetriever: IEmailsRetriever) {
|
constructor(logger: IRequestLogger, usersDatabase: IUsersDatabase) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.emailsRetriever = emailsRetriever;
|
this.usersDatabase = usersDatabase;
|
||||||
}
|
}
|
||||||
|
|
||||||
challenge(): string {
|
challenge(): string {
|
||||||
|
@ -36,7 +36,7 @@ export default class PasswordResetHandler implements IdentityValidable {
|
||||||
return BluebirdPromise.reject(
|
return BluebirdPromise.reject(
|
||||||
new exceptions.AccessDeniedError("No user id provided"));
|
new exceptions.AccessDeniedError("No user id provided"));
|
||||||
|
|
||||||
return that.emailsRetriever.retrieve(userid);
|
return that.usersDatabase.getEmails(userid);
|
||||||
})
|
})
|
||||||
.then(function (emails: string[]) {
|
.then(function (emails: string[]) {
|
||||||
if (!emails && emails.length <= 0) throw new Error("No email found");
|
if (!emails && emails.length <= 0) throw new Error("No email found");
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
|
|
||||||
import Assert = require("assert");
|
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 VerifyGet = require("./get");
|
||||||
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession } from "../../../../types/AuthenticationSession";
|
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 ExpressMock = require("../../stubs/express.spec");
|
||||||
import { ServerVariables } from "../../ServerVariables";
|
import { ServerVariables } from "../../ServerVariables";
|
||||||
import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../ServerVariablesMockBuilder.spec";
|
import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../ServerVariablesMockBuilder.spec";
|
||||||
|
@ -44,7 +45,7 @@ describe("routes/verify/get", function () {
|
||||||
authSession.second_factor = true;
|
authSession.second_factor = true;
|
||||||
authSession.userid = "myuser";
|
authSession.userid = "myuser";
|
||||||
authSession.groups = ["mygroup", "othergroup"];
|
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 () {
|
.then(function () {
|
||||||
Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "myuser");
|
Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "myuser");
|
||||||
Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup");
|
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) {
|
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 () {
|
.then(function () {
|
||||||
Assert.equal(status_code, res.status.getCall(0).args[0]);
|
Assert.equal(status_code, res.status.getCall(0).args[0]);
|
||||||
});
|
});
|
||||||
|
@ -156,7 +157,7 @@ describe("routes/verify/get", function () {
|
||||||
mocks.accessController.isAccessAllowedMock.returns(true);
|
mocks.accessController.isAccessAllowedMock.returns(true);
|
||||||
authSession.first_factor = true;
|
authSession.first_factor = true;
|
||||||
authSession.userid = "user1";
|
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 () {
|
.then(function () {
|
||||||
Assert(res.status.calledWith(204));
|
Assert(res.status.calledWith(204));
|
||||||
Assert(res.send.calledOnce);
|
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 () {
|
it("should be rejected with 401 when first factor is not validated", function () {
|
||||||
mocks.accessController.isAccessAllowedMock.returns(true);
|
mocks.accessController.isAccessAllowedMock.returns(true);
|
||||||
authSession.first_factor = false;
|
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 () {
|
.then(function () {
|
||||||
Assert(res.status.calledWith(401));
|
Assert(res.status.calledWith(401));
|
||||||
});
|
});
|
||||||
|
@ -184,7 +185,7 @@ describe("routes/verify/get", function () {
|
||||||
authSession.userid = "myuser";
|
authSession.userid = "myuser";
|
||||||
authSession.groups = ["mygroup", "othergroup"];
|
authSession.groups = ["mygroup", "othergroup"];
|
||||||
authSession.last_activity_datetime = currentTime;
|
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 () {
|
.then(function () {
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger);
|
return AuthenticationSessionHandler.get(req as any, vars.logger);
|
||||||
})
|
})
|
||||||
|
@ -203,7 +204,7 @@ describe("routes/verify/get", function () {
|
||||||
authSession.userid = "myuser";
|
authSession.userid = "myuser";
|
||||||
authSession.groups = ["mygroup", "othergroup"];
|
authSession.groups = ["mygroup", "othergroup"];
|
||||||
authSession.last_activity_datetime = currentTime;
|
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 () {
|
.then(function () {
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger);
|
return AuthenticationSessionHandler.get(req as any, vars.logger);
|
||||||
})
|
})
|
||||||
|
@ -220,11 +221,11 @@ describe("routes/verify/get", function () {
|
||||||
it("should return error code 401", function() {
|
it("should return error code 401", function() {
|
||||||
mocks.accessController.isAccessAllowedMock.returns(true);
|
mocks.accessController.isAccessAllowedMock.returns(true);
|
||||||
mocks.config.authentication_methods.default_method = "single_factor";
|
mocks.config.authentication_methods.default_method = "single_factor";
|
||||||
mocks.ldapAuthenticator.authenticateStub.rejects(new Error(
|
mocks.usersDatabase.checkUserPasswordStub.rejects(new Error(
|
||||||
"Invalid credentials"));
|
"Invalid credentials"));
|
||||||
req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA==";
|
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 () {
|
.then(function () {
|
||||||
Assert(res.status.calledWithExactly(401));
|
Assert(res.status.calledWithExactly(401));
|
||||||
});
|
});
|
||||||
|
@ -234,12 +235,12 @@ describe("routes/verify/get", function () {
|
||||||
const REDIRECT_URL = "http://redirection_url.com";
|
const REDIRECT_URL = "http://redirection_url.com";
|
||||||
mocks.accessController.isAccessAllowedMock.returns(true);
|
mocks.accessController.isAccessAllowedMock.returns(true);
|
||||||
mocks.config.authentication_methods.default_method = "single_factor";
|
mocks.config.authentication_methods.default_method = "single_factor";
|
||||||
mocks.ldapAuthenticator.authenticateStub.rejects(new Error(
|
mocks.usersDatabase.checkUserPasswordStub.rejects(new Error(
|
||||||
"Invalid credentials"));
|
"Invalid credentials"));
|
||||||
req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA==";
|
req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA==";
|
||||||
req.query["rd"] = REDIRECT_URL;
|
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 () {
|
.then(function () {
|
||||||
Assert(res.redirect.calledWithExactly(REDIRECT_URL));
|
Assert(res.redirect.calledWithExactly(REDIRECT_URL));
|
||||||
});
|
});
|
||||||
|
@ -250,12 +251,12 @@ describe("routes/verify/get", function () {
|
||||||
it("should authenticate correctly", function () {
|
it("should authenticate correctly", function () {
|
||||||
mocks.accessController.isAccessAllowedMock.returns(true);
|
mocks.accessController.isAccessAllowedMock.returns(true);
|
||||||
mocks.config.authentication_methods.default_method = "single_factor";
|
mocks.config.authentication_methods.default_method = "single_factor";
|
||||||
mocks.ldapAuthenticator.authenticateStub.returns({
|
mocks.usersDatabase.checkUserPasswordStub.returns({
|
||||||
groups: ["mygroup", "othergroup"],
|
groups: ["mygroup", "othergroup"],
|
||||||
});
|
});
|
||||||
req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA==";
|
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 () {
|
.then(function () {
|
||||||
Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "john");
|
Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "john");
|
||||||
Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup");
|
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 = {
|
mocks.config.authentication_methods.per_subdomain_methods = {
|
||||||
"secret.example.com": "two_factor"
|
"secret.example.com": "two_factor"
|
||||||
};
|
};
|
||||||
mocks.ldapAuthenticator.authenticateStub.resolves({
|
mocks.usersDatabase.checkUserPasswordStub.resolves({
|
||||||
groups: ["mygroup", "othergroup"],
|
groups: ["mygroup", "othergroup"],
|
||||||
});
|
});
|
||||||
req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA==";
|
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 () {
|
.then(function () {
|
||||||
Assert(res.status.calledWithExactly(401));
|
Assert(res.status.calledWithExactly(401));
|
||||||
});
|
});
|
||||||
|
@ -283,12 +284,12 @@ describe("routes/verify/get", function () {
|
||||||
it("should fail when base64 token is not valid", function () {
|
it("should fail when base64 token is not valid", function () {
|
||||||
mocks.accessController.isAccessAllowedMock.returns(true);
|
mocks.accessController.isAccessAllowedMock.returns(true);
|
||||||
mocks.config.authentication_methods.default_method = "single_factor";
|
mocks.config.authentication_methods.default_method = "single_factor";
|
||||||
mocks.ldapAuthenticator.authenticateStub.resolves({
|
mocks.usersDatabase.checkUserPasswordStub.resolves({
|
||||||
groups: ["mygroup", "othergroup"],
|
groups: ["mygroup", "othergroup"],
|
||||||
});
|
});
|
||||||
req.headers["proxy-authorization"] = "Basic i_m*not_a_base64*token";
|
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 () {
|
.then(function () {
|
||||||
Assert(res.status.calledWithExactly(401));
|
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 () {
|
it("should fail when base64 token has not format user:psswd", function () {
|
||||||
mocks.accessController.isAccessAllowedMock.returns(true);
|
mocks.accessController.isAccessAllowedMock.returns(true);
|
||||||
mocks.config.authentication_methods.default_method = "single_factor";
|
mocks.config.authentication_methods.default_method = "single_factor";
|
||||||
mocks.ldapAuthenticator.authenticateStub.resolves({
|
mocks.usersDatabase.checkUserPasswordStub.resolves({
|
||||||
groups: ["mygroup", "othergroup"],
|
groups: ["mygroup", "othergroup"],
|
||||||
});
|
});
|
||||||
req.headers["proxy-authorization"] = "Basic am9objpwYXNzOmJhZA==";
|
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 () {
|
.then(function () {
|
||||||
Assert(res.status.calledWithExactly(401));
|
Assert(res.status.calledWithExactly(401));
|
||||||
});
|
});
|
||||||
|
@ -311,11 +312,11 @@ describe("routes/verify/get", function () {
|
||||||
it("should fail when bad user password is provided", function () {
|
it("should fail when bad user password is provided", function () {
|
||||||
mocks.accessController.isAccessAllowedMock.returns(true);
|
mocks.accessController.isAccessAllowedMock.returns(true);
|
||||||
mocks.config.authentication_methods.default_method = "single_factor";
|
mocks.config.authentication_methods.default_method = "single_factor";
|
||||||
mocks.ldapAuthenticator.authenticateStub.rejects(new Error(
|
mocks.usersDatabase.checkUserPasswordStub.rejects(new Error(
|
||||||
"Invalid credentials"));
|
"Invalid credentials"));
|
||||||
req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA==";
|
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 () {
|
.then(function () {
|
||||||
Assert(res.status.calledWithExactly(401));
|
Assert(res.status.calledWithExactly(401));
|
||||||
});
|
});
|
||||||
|
@ -324,12 +325,12 @@ describe("routes/verify/get", function () {
|
||||||
it("should fail when resource is restricted", function () {
|
it("should fail when resource is restricted", function () {
|
||||||
mocks.accessController.isAccessAllowedMock.returns(false);
|
mocks.accessController.isAccessAllowedMock.returns(false);
|
||||||
mocks.config.authentication_methods.default_method = "single_factor";
|
mocks.config.authentication_methods.default_method = "single_factor";
|
||||||
mocks.ldapAuthenticator.authenticateStub.resolves({
|
mocks.usersDatabase.checkUserPasswordStub.resolves({
|
||||||
groups: ["mygroup", "othergroup"],
|
groups: ["mygroup", "othergroup"],
|
||||||
});
|
});
|
||||||
req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA==";
|
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 () {
|
.then(function () {
|
||||||
Assert(res.status.calledWithExactly(401));
|
Assert(res.status.calledWithExactly(401));
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,11 +12,11 @@ export default function (req: Express.Request, res: Express.Response,
|
||||||
vars: ServerVariables, authorizationHeader: string)
|
vars: ServerVariables, authorizationHeader: string)
|
||||||
: BluebirdPromise<{ username: string, groups: string[] }> {
|
: BluebirdPromise<{ username: string, groups: string[] }> {
|
||||||
let username: string;
|
let username: string;
|
||||||
let groups: string[];
|
|
||||||
let domain: string;
|
let domain: string;
|
||||||
let originalUri: string;
|
let originalUri: string;
|
||||||
|
|
||||||
return new BluebirdPromise<[string, string]>(function (resolve, reject) {
|
return BluebirdPromise.resolve()
|
||||||
|
.then(() => {
|
||||||
const originalUrl = ObjectPath.get<Express.Request, string>(req, "headers.x-original-url");
|
const originalUrl = ObjectPath.get<Express.Request, string>(req, "headers.x-original-url");
|
||||||
domain = DomainExtractor.fromUrl(originalUrl);
|
domain = DomainExtractor.fromUrl(originalUrl);
|
||||||
originalUri =
|
originalUri =
|
||||||
|
@ -25,9 +25,8 @@ export default function (req: Express.Request, res: Express.Response,
|
||||||
MethodCalculator.compute(vars.config.authentication_methods, domain);
|
MethodCalculator.compute(vars.config.authentication_methods, domain);
|
||||||
|
|
||||||
if (authenticationMethod != "single_factor") {
|
if (authenticationMethod != "single_factor") {
|
||||||
reject(new Error("This domain is not protected with single factor. " +
|
return BluebirdPromise.reject(new Error("This domain is not protected with single factor. " +
|
||||||
"You cannot log in with basic authentication."));
|
"You cannot log in with basic authentication."));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const base64Re = new RegExp("^Basic ((?:[A-Za-z0-9+/]{4})*" +
|
const base64Re = new RegExp("^Basic ((?:[A-Za-z0-9+/]{4})*" +
|
||||||
|
@ -35,8 +34,7 @@ export default function (req: Express.Request, res: Express.Response,
|
||||||
const isTokenValidBase64 = base64Re.test(authorizationHeader);
|
const isTokenValidBase64 = base64Re.test(authorizationHeader);
|
||||||
|
|
||||||
if (!isTokenValidBase64) {
|
if (!isTokenValidBase64) {
|
||||||
reject(new Error("No valid base64 token found in the header"));
|
return BluebirdPromise.reject(new Error("No valid base64 token found in the header"));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokenMatches = authorizationHeader.match(base64Re);
|
const tokenMatches = authorizationHeader.match(base64Re);
|
||||||
|
@ -45,27 +43,20 @@ export default function (req: Express.Request, res: Express.Response,
|
||||||
const splittedToken = decodedToken.split(":");
|
const splittedToken = decodedToken.split(":");
|
||||||
|
|
||||||
if (splittedToken.length != 2) {
|
if (splittedToken.length != 2) {
|
||||||
reject(new Error(
|
return BluebirdPromise.reject(new Error(
|
||||||
"The authorization token is invalid. Expecting 'userid:password'"));
|
"The authorization token is invalid. Expecting 'userid:password'"));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
username = splittedToken[0];
|
username = splittedToken[0];
|
||||||
const password = splittedToken[1];
|
const password = splittedToken[1];
|
||||||
resolve([username, password]);
|
return vars.usersDatabase.checkUserPassword(username, password);
|
||||||
})
|
|
||||||
.then(function ([userid, password]) {
|
|
||||||
return vars.ldapAuthenticator.authenticate(userid, password);
|
|
||||||
})
|
})
|
||||||
.then(function (groupsAndEmails) {
|
.then(function (groupsAndEmails) {
|
||||||
groups = groupsAndEmails.groups;
|
return AccessControl(req, vars, domain, originalUri, username, groupsAndEmails.groups)
|
||||||
return AccessControl(req, vars, domain, originalUri, username, groups);
|
.then(() => BluebirdPromise.resolve({
|
||||||
})
|
|
||||||
.then(function () {
|
|
||||||
return BluebirdPromise.resolve({
|
|
||||||
username: username,
|
username: username,
|
||||||
groups: groups
|
groups: groupsAndEmails.groups
|
||||||
});
|
}));
|
||||||
})
|
})
|
||||||
.catch(function (err: Error) {
|
.catch(function (err: Error) {
|
||||||
return BluebirdPromise.reject(
|
return BluebirdPromise.reject(
|
||||||
|
|
|
@ -104,7 +104,7 @@ function setupResetPassword(app: Express.Application, vars: ServerVariables) {
|
||||||
IdentityCheckMiddleware.register(app,
|
IdentityCheckMiddleware.register(app,
|
||||||
Endpoints.RESET_PASSWORD_IDENTITY_START_GET,
|
Endpoints.RESET_PASSWORD_IDENTITY_START_GET,
|
||||||
Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET,
|
Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET,
|
||||||
new ResetPasswordIdentityHandler(vars.logger, vars.ldapEmailsRetriever),
|
new ResetPasswordIdentityHandler(vars.logger, vars.usersDatabase),
|
||||||
vars);
|
vars);
|
||||||
|
|
||||||
app.get(Endpoints.RESET_PASSWORD_REQUEST_GET,
|
app.get(Endpoints.RESET_PASSWORD_REQUEST_GET,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user