LDAP support, courtesy of an anonymous donator

This commit is contained in:
bgamard 2020-08-28 18:09:54 +02:00
parent 6dc4f1b448
commit a9719feeec
20 changed files with 597 additions and 6 deletions

View File

@ -131,7 +131,12 @@
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-all</artifactId>
</dependency>
<!-- Only there to read old index and rebuild them -->
<dependency>
<groupId>org.apache.lucene</groupId>

View File

@ -44,5 +44,18 @@ public enum ConfigType {
INBOX_PASSWORD,
INBOX_TAG,
INBOX_AUTOMATIC_TAGS,
INBOX_DELETE_IMPORTED
INBOX_DELETE_IMPORTED,
/**
* LDAP connection.
*/
LDAP_ENABLED,
LDAP_HOST,
LDAP_PORT,
LDAP_ADMIN_DN,
LDAP_ADMIN_PASSWORD,
LDAP_BASE_DN,
LDAP_FILTER,
LDAP_DEFAULT_EMAIL,
LDAP_DEFAULT_STORAGE
}

View File

@ -50,6 +50,19 @@ public class ConfigUtil {
return Integer.parseInt(value);
}
/**
* Returns the long value of a configuration parameter.
*
* @param configType Type of the configuration parameter
* @return Long value of the configuration parameter
* @throws IllegalStateException Configuration parameter undefined
*/
public static long getConfigLongValue(ConfigType configType) {
String value = getConfigStringValue(configType);
return Long.parseLong(value);
}
/**
* Returns the boolean value of a configuration parameter.
*

View File

@ -0,0 +1,131 @@
package com.sismics.docs.core.util.authentication;
import com.sismics.docs.core.constant.ConfigType;
import com.sismics.docs.core.constant.Constants;
import com.sismics.docs.core.dao.ConfigDao;
import com.sismics.docs.core.dao.UserDao;
import com.sismics.docs.core.model.jpa.Config;
import com.sismics.docs.core.model.jpa.User;
import com.sismics.docs.core.util.ConfigUtil;
import com.sismics.util.ClasspathScanner;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
import org.apache.directory.api.ldap.model.entry.Attribute;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.entry.Value;
import org.apache.directory.api.ldap.model.message.SearchScope;
import org.apache.directory.ldap.client.api.DefaultLdapConnectionFactory;
import org.apache.directory.ldap.client.api.LdapConnectionConfig;
import org.apache.directory.ldap.client.api.LdapConnectionPool;
import org.apache.directory.ldap.client.api.PoolableLdapConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.UUID;
/**
* LDAP authentication handler.
*
* @author bgamard
*/
@ClasspathScanner.Priority(50) // Before the internal database
public class LdapAuthenticationHandler implements AuthenticationHandler {
/**
* Logger.
*/
private static final Logger log = LoggerFactory.getLogger(LdapAuthenticationHandler.class);
/**
* LDAP connection pool.
*/
private static LdapConnectionPool pool;
/**
* Reset the LDAP pool.
*/
public static void reset() {
if (pool != null) {
try {
pool.close();
} catch (Exception e) {
// NOP
}
}
pool = null;
}
/**
* Initialize the LDAP pool.
*/
private static void init() {
ConfigDao configDao = new ConfigDao();
Config ldapEnabled = configDao.getById(ConfigType.LDAP_ENABLED);
if (pool != null || ldapEnabled == null || !Boolean.parseBoolean(ldapEnabled.getValue())) {
return;
}
LdapConnectionConfig config = new LdapConnectionConfig();
config.setLdapHost(ConfigUtil.getConfigStringValue(ConfigType.LDAP_HOST));
config.setLdapPort(ConfigUtil.getConfigIntegerValue(ConfigType.LDAP_PORT));
config.setName(ConfigUtil.getConfigStringValue(ConfigType.LDAP_ADMIN_DN));
config.setCredentials(ConfigUtil.getConfigStringValue(ConfigType.LDAP_ADMIN_PASSWORD));
DefaultLdapConnectionFactory factory = new DefaultLdapConnectionFactory(config);
GenericObjectPool.Config poolConfig = new GenericObjectPool.Config();
poolConfig.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
poolConfig.maxWait = 500;
pool = new LdapConnectionPool(new PoolableLdapConnectionFactory(factory), poolConfig);
}
@Override
public User authenticate(String username, String password) {
init();
if (pool == null) {
return null;
}
// Fetch and authenticate the user
Entry userEntry;
try {
EntryCursor cursor = pool.getConnection().search(ConfigUtil.getConfigStringValue(ConfigType.LDAP_BASE_DN),
ConfigUtil.getConfigStringValue(ConfigType.LDAP_FILTER).replace("USERNAME", username), SearchScope.SUBTREE);
if (cursor.next()) {
userEntry = cursor.get();
pool.getConnection().bind(userEntry.getDn(), password);
} else {
// User not found
return null;
}
} catch (Exception e) {
log.error("Error authenticating \"" + username + "\" using the LDAP", e);
return null;
}
UserDao userDao = new UserDao();
User user = userDao.getActiveByUsername(username);
if (user == null) {
// The user is valid but never authenticated, create the user now
log.info("\"" + username + "\" authenticated for the first time, creating the internal user");
user = new User();
user.setRoleId(Constants.DEFAULT_USER_ROLE);
user.setUsername(username);
user.setPassword(UUID.randomUUID().toString()); // No authentication using the internal database
Attribute mailAttribute = userEntry.get("mail");
if (mailAttribute == null || mailAttribute.get() == null) {
user.setEmail(ConfigUtil.getConfigStringValue(ConfigType.LDAP_DEFAULT_EMAIL));
} else {
Value<?> value = mailAttribute.get();
user.setEmail(value.getString());
}
user.setStorageQuota(ConfigUtil.getConfigLongValue(ConfigType.LDAP_DEFAULT_STORAGE));
try {
userDao.create(user, "admin");
} catch (Exception e) {
log.error("Error while creating the internal user", e);
return null;
}
}
return user;
}
}

View File

@ -6,4 +6,5 @@ log4j.appender.MEMORY=com.sismics.util.log4j.MemoryAppender
log4j.appender.MEMORY.size=1000
log4j.logger.com.sismics=INFO
log4j.logger.org.hibernate=ERROR
log4j.logger.org.hibernate=ERROR
log4j.logger.org.apache.directory=ERROR

View File

@ -5,6 +5,7 @@ import com.sismics.util.filter.TokenBasedSecurityFilter;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.media.multipart.file.StreamDataBodyPart;
import org.junit.Assert;
import javax.json.JsonObject;
import javax.ws.rs.client.Entity;
@ -113,7 +114,8 @@ public class ClientUtil {
.param("username", username)
.param("password", password)
.param("remember", remember.toString())));
Assert.assertEquals(200, response.getStatus());
return getAuthenticationCookie(response);
}

View File

@ -7,4 +7,5 @@ log4j.appender.MEMORY.size=1000
log4j.logger.com.sismics=DEBUG
log4j.logger.org.apache.pdfbox=ERROR
log4j.logger.org.glassfish.jersey.servlet.WebComponent=ERROR
log4j.logger.org.glassfish.jersey.servlet.WebComponent=ERROR
log4j.logger.org.apache.directory=ERROR

View File

@ -14,6 +14,7 @@ import com.sismics.docs.core.model.jpa.File;
import com.sismics.docs.core.service.InboxService;
import com.sismics.docs.core.util.ConfigUtil;
import com.sismics.docs.core.util.DirectoryUtil;
import com.sismics.docs.core.util.authentication.LdapAuthenticationHandler;
import com.sismics.docs.core.util.jpa.PaginatedList;
import com.sismics.docs.core.util.jpa.PaginatedLists;
import com.sismics.docs.rest.constant.BaseFunction;
@ -696,4 +697,138 @@ public class AppResource extends BaseResource {
.add("status", "ok");
return Response.ok().entity(response.build()).build();
}
/**
* Get the LDAP authentication configuration.
*
* @api {get} /app/config_ldap Get the LDAP authentication configuration
* @apiName GetAppConfigLdap
* @apiGroup App
* @apiSuccess {Boolean} enabled LDAP authentication enabled
* @apiSuccess {String} host LDAP server host
* @apiSuccess {Integer} port LDAP server port
* @apiSuccess {String} admin_dn Admin DN
* @apiSuccess {String} admin_password Admin password
* @apiSuccess {String} base_dn Base DN
* @apiSuccess {String} filter LDAP filter
* @apiSuccess {String} default_email LDAP default email
* @apiSuccess {Integer} default_storage LDAP default storage
* @apiError (client) ForbiddenError Access denied
* @apiPermission admin
* @apiVersion 1.9.0
*
* @return Response
*/
@GET
@Path("config_ldap")
public Response getConfigLdap() {
if (!authenticate()) {
throw new ForbiddenClientException();
}
checkBaseFunction(BaseFunction.ADMIN);
ConfigDao configDao = new ConfigDao();
Config enabled = configDao.getById(ConfigType.LDAP_ENABLED);
JsonObjectBuilder response = Json.createObjectBuilder();
if (enabled != null && Boolean.parseBoolean(enabled.getValue())) {
// LDAP enabled
response.add("enabled", true)
.add("host", ConfigUtil.getConfigStringValue(ConfigType.LDAP_HOST))
.add("port", ConfigUtil.getConfigIntegerValue(ConfigType.LDAP_PORT))
.add("admin_dn", ConfigUtil.getConfigStringValue(ConfigType.LDAP_ADMIN_DN))
.add("admin_password", ConfigUtil.getConfigStringValue(ConfigType.LDAP_ADMIN_PASSWORD))
.add("base_dn", ConfigUtil.getConfigStringValue(ConfigType.LDAP_BASE_DN))
.add("filter", ConfigUtil.getConfigStringValue(ConfigType.LDAP_FILTER))
.add("default_email", ConfigUtil.getConfigStringValue(ConfigType.LDAP_DEFAULT_EMAIL))
.add("default_storage", ConfigUtil.getConfigLongValue(ConfigType.LDAP_DEFAULT_STORAGE));
} else {
// LDAP disabled
response.add("enabled", false);
}
return Response.ok().entity(response.build()).build();
}
/**
* Configure the LDAP authentication.
*
* @api {post} /app/config_ldap Configure the LDAP authentication
* @apiName PostAppConfigLdap
* @apiGroup App
* @apiParam {Boolean} enabled LDAP authentication enabled
* @apiParam {String} host LDAP server host
* @apiParam {Integer} port LDAP server port
* @apiParam {String} admin_dn Admin DN
* @apiParam {String} admin_password Admin password
* @apiParam {String} base_dn Base DN
* @apiParam {String} filter LDAP filter
* @apiParam {String} default_email LDAP default email
* @apiParam {Integer} default_storage LDAP default storage
* @apiError (client) ForbiddenError Access denied
* @apiError (client) ValidationError Validation error
* @apiPermission admin
* @apiVersion 1.9.0
*
* @param enabled LDAP authentication enabled
* @param host LDAP server host
* @param portStr LDAP server port
* @param adminDn Admin DN
* @param adminPassword Admin password
* @param baseDn Base DN
* @param filter LDAP filter
* @param defaultEmail LDAP default email
* @param defaultStorageStr LDAP default storage
* @return Response
*/
@POST
@Path("config_ldap")
public Response configLdap(@FormParam("enabled") Boolean enabled,
@FormParam("host") String host,
@FormParam("port") String portStr,
@FormParam("admin_dn") String adminDn,
@FormParam("admin_password") String adminPassword,
@FormParam("base_dn") String baseDn,
@FormParam("filter") String filter,
@FormParam("default_email") String defaultEmail,
@FormParam("default_storage") String defaultStorageStr) {
if (!authenticate()) {
throw new ForbiddenClientException();
}
checkBaseFunction(BaseFunction.ADMIN);
ConfigDao configDao = new ConfigDao();
if (enabled != null && enabled) {
// LDAP enabled, validate everything
ValidationUtil.validateLength(host, "host", 1, 250);
ValidationUtil.validateInteger(portStr, "port");
ValidationUtil.validateLength(adminDn, "admin_dn", 1, 250);
ValidationUtil.validateLength(adminPassword, "admin_password", 1, 250);
ValidationUtil.validateLength(baseDn, "base_dn", 1, 250);
ValidationUtil.validateLength(filter, "filter", 1, 250);
if (!filter.contains("USERNAME")) {
throw new ClientException("ValidationError", "'filter' must contains 'USERNAME'");
}
ValidationUtil.validateLength(defaultEmail, "default_email", 1, 250);
ValidationUtil.validateLong(defaultStorageStr, "default_storage");
configDao.update(ConfigType.LDAP_ENABLED, Boolean.TRUE.toString());
configDao.update(ConfigType.LDAP_HOST, host);
configDao.update(ConfigType.LDAP_PORT, portStr);
configDao.update(ConfigType.LDAP_ADMIN_DN, adminDn);
configDao.update(ConfigType.LDAP_ADMIN_PASSWORD, adminPassword);
configDao.update(ConfigType.LDAP_BASE_DN, baseDn);
configDao.update(ConfigType.LDAP_FILTER, filter);
configDao.update(ConfigType.LDAP_DEFAULT_EMAIL, defaultEmail);
configDao.update(ConfigType.LDAP_DEFAULT_STORAGE, defaultStorageStr);
} else {
// LDAP disabled
configDao.update(ConfigType.LDAP_ENABLED, Boolean.FALSE.toString());
}
// Reset the LDAP pool to reconnect with the new configuration
LdapAuthenticationHandler.reset();
return Response.ok().build();
}
}

View File

@ -244,6 +244,15 @@ angular.module('docs',
}
}
})
.state('settings.ldap', {
url: '/ldap',
views: {
'settings': {
templateUrl: 'partial/docs/settings.ldap.html',
controller: 'SettingsLdap'
}
}
})
.state('document', {
url: '/document',
abstract: true,

View File

@ -0,0 +1,33 @@
'use strict';
/**
* Settings LDAP page controller.
*/
angular.module('docs').controller('SettingsLdap', function($scope, Restangular, $translate, $timeout) {
$scope.ldap = {
enabled: false
};
// Get the LDAP configuration
Restangular.one('app/config_ldap').get().then(function (data) {
$scope.ldap = data;
if ($scope.ldap.default_storage) {
$scope.ldap.default_storage /= 1000000;
}
});
// Edit SMTP config
$scope.saveResult = undefined;
$scope.save = function () {
var ldap = angular.copy($scope.ldap);
if (ldap.default_storage) {
ldap.default_storage *= 1000000;
}
Restangular.one('app').post('config_ldap', ldap).then(function () {
$scope.saveResult = $translate.instant('settings.ldap.saved');
$timeout(function() {
$scope.saveResult = undefined;
}, 5000);
});
};
});

View File

@ -94,6 +94,7 @@
<script src="app/docs/controller/settings/SettingsGroupEdit.js" type="text/javascript"></script>
<script src="app/docs/controller/settings/SettingsVocabulary.js" type="text/javascript"></script>
<script src="app/docs/controller/settings/SettingsMetadata.js" type="text/javascript"></script>
<script src="app/docs/controller/settings/SettingsLdap.js" type="text/javascript"></script>
<script src="app/docs/controller/usergroup/UserGroup.js" type="text/javascript"></script>
<script src="app/docs/controller/usergroup/UserProfile.js" type="text/javascript"></script>
<script src="app/docs/controller/usergroup/GroupProfile.js" type="text/javascript"></script>

View File

@ -278,8 +278,22 @@
"menu_vocabularies": "Vocabularies",
"menu_configuration": "Configuration",
"menu_inbox": "Inbox scanning",
"menu_ldap": "LDAP authentication",
"menu_metadata": "Custom metadata",
"menu_monitoring": "Monitoring",
"ldap": {
"title": "LDAP authentication",
"enabled": "Enable LDAP authentication",
"host": "LDAP hostname",
"port": "LDAP port (389 by default)",
"admin_dn": "Admin DN",
"admin_password": "Admin password",
"base_dn": "Base search DN",
"filter": "Search filter (must contains USERNAME, eg. \"(uid=USERNAME)\")",
"default_email": "Default email for LDAP user",
"default_storage": "Default storage for LDAP user",
"saved": "LDAP configuration saved successfully"
},
"user": {
"title": "Users management",
"add_user": "Add a user",

View File

@ -278,7 +278,21 @@
"menu_vocabularies": "Vocabulaires",
"menu_configuration": "Configuration",
"menu_inbox": "Scanning de boîte de réception",
"menu_ldap": "Authentification LDAP",
"menu_monitoring": "Monitoring",
"ldap": {
"title": "Authentification LDAP",
"enabled": "Activer l'authentication LDAP",
"host": "Hôte LDAP",
"port": "Port LDAP (389 par défaut)",
"admin_dn": "DN administrateur",
"admin_password": "Mot de passe administrateur",
"base_dn": "DN de recherche",
"filter": "Filtre de recherche (doit contenir USERNAME, cf. \"(uid=USERNAME)\")",
"default_email": "Email par défaut pour les utilisateurs LDAP",
"default_storage": "Stockage par défaut pour les utilisateurs LDAP",
"saved": "Configuration IMAP sauvegardée avec succès"
},
"user": {
"title": "Gestion des utilisateurs",
"add_user": "Ajouter un utilisateur",

View File

@ -20,6 +20,7 @@
<a class="list-group-item" ui-sref-active="{ active: 'settings.config.**' }" href="#/settings/config">{{ 'settings.menu_configuration' | translate }}</a>
<a class="list-group-item" ui-sref-active="{ active: 'settings.metadata.**' }" href="#/settings/metadata">{{ 'settings.menu_metadata' | translate }}</a>
<a class="list-group-item" ui-sref-active="{ active: 'settings.inbox.**' }" href="#/settings/inbox">{{ 'settings.menu_inbox' | translate }}</a>
<a class="list-group-item" ui-sref-active="{ active: 'settings.ldap.**' }" href="#/settings/ldap">{{ 'settings.menu_ldap' | translate }}</a>
<a class="list-group-item" ui-sref-active="{ active: 'settings.monitoring.**' }" href="#/settings/monitoring">{{ 'settings.menu_monitoring' | translate }}</a>
</ul>
</div>

View File

@ -0,0 +1,88 @@
<h2 translate="settings.ldap.title"></h2>
<form class="form-horizontal" name="form" novalidate>
<div class="form-group">
<label class="col-sm-2 control-label" for="ldapEnabled">{{ 'settings.ldap.enabled' | translate }}</label>
<div class="col-sm-7">
<input name="enabled" type="checkbox" id="ldapEnabled" ng-model="ldap.enabled" />
</div>
</div>
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.hostname.$valid && form.$dirty }">
<label class="col-sm-2 control-label" for="ldapHost">{{ 'settings.ldap.host' | translate }}</label>
<div class="col-sm-7">
<input name="hostname" type="text" class="form-control" id="ldapHost" ng-model="ldap.host" ng-maxlength="250" required />
</div>
</div>
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.port.$valid && form.$dirty }">
<label class="col-sm-2 control-label" for="ldapPort">{{ 'settings.ldap.port' | translate }}</label>
<div class="col-sm-7">
<input name="port" type="number" class="form-control" id="ldapPort" ng-model="ldap.port" required />
</div>
</div>
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.adminDn.$valid && form.$dirty }">
<label class="col-sm-2 control-label" for="ldapAdminDn">{{ 'settings.ldap.admin_dn' | translate }}</label>
<div class="col-sm-7">
<input name="adminDn" type="text" class="form-control" id="ldapAdminDn" ng-model="ldap.admin_dn" ng-maxlength="250" required />
</div>
</div>
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.adminPassword.$valid && form.$dirty }">
<label class="col-sm-2 control-label" for="ldapAdminPassword">{{ 'settings.ldap.admin_password' | translate }}</label>
<div class="col-sm-7">
<input name="adminPassword" type="password" class="form-control" id="ldapAdminPassword" ng-model="ldap.admin_password" ng-maxlength="250" required />
</div>
</div>
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.baseDn.$valid && form.$dirty }">
<label class="col-sm-2 control-label" for="ldapBaseDn">{{ 'settings.ldap.base_dn' | translate }}</label>
<div class="col-sm-7">
<input name="baseDn" type="text" class="form-control" id="ldapBaseDn" ng-model="ldap.base_dn" ng-maxlength="250" required />
</div>
</div>
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.filter.$valid && form.$dirty }">
<label class="col-sm-2 control-label" for="ldapFilter">{{ 'settings.ldap.filter' | translate }}</label>
<div class="col-sm-7">
<input name="filter" type="text" class="form-control" id="ldapFilter"
ng-pattern="/(USERNAME)+/"
ng-model="ldap.filter" ng-maxlength="250" required />
</div>
</div>
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.defaultEmail.$valid && form.$dirty }">
<label class="col-sm-2 control-label" for="ldapDefaultEmail">{{ 'settings.ldap.default_email' | translate }}</label>
<div class="col-sm-7">
<input name="defaultEmail" type="text" class="form-control" id="ldapDefaultEmail" ng-model="ldap.default_email" ng-maxlength="250" required />
</div>
</div>
<div ng-if="ldap.enabled" class="form-group" ng-class="{ 'has-error': !form.defaultStorage.$valid && form.$dirty }">
<label class="col-sm-2 control-label" for="ldapDefaultStorage">{{ 'settings.ldap.default_storage' | translate }}</label>
<div class="col-sm-7">
<div class="input-group">
<input name="defaultStorage" type="number" id="ldapDefaultStorage" required class="form-control"
ng-pattern="/[0-9]*/" ng-model="ldap.default_storage"/>
<div class="input-group-addon">{{ 'filter.filesize.mb' | translate }}</div>
</div>
</div>
<div class="col-sm-3">
<span class="help-block" ng-show="form.default_storage.$error.pattern && form.$dirty">{{ 'validation.number' | translate }}</span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary" ng-click="save()" ng-disabled="!form.$valid">
<span class="fas fa-pencil-alt"></span> {{ 'save' | translate }}
</button>
</div>
</div>
</form>
<div class="alert col-sm-9 alert-success"
ng-show="saveResult">
{{ saveResult }}
</div>

View File

@ -7,4 +7,5 @@ log4j.appender.MEMORY.size=1000
log4j.logger.com.sismics=INFO
log4j.logger.org.apache.pdfbox=ERROR
log4j.logger.org.glassfish.jersey.servlet.WebComponent=ERROR
log4j.logger.org.glassfish.jersey.servlet.WebComponent=ERROR
log4j.logger.org.apache.directory=ERROR

View File

@ -1,10 +1,20 @@
package com.sismics.docs.rest;
import com.google.common.io.Resources;
import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.GreenMailUtil;
import com.icegreen.greenmail.util.ServerSetup;
import com.sismics.docs.core.model.context.AppContext;
import com.sismics.util.filter.TokenBasedSecurityFilter;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.server.core.api.DirectoryService;
import org.apache.directory.server.core.api.partition.Partition;
import org.apache.directory.server.core.factory.DefaultDirectoryServiceFactory;
import org.apache.directory.server.core.factory.DirectoryServiceFactory;
import org.apache.directory.server.core.partition.impl.avl.AvlPartition;
import org.apache.directory.server.ldap.LdapServer;
import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
import org.apache.directory.server.protocol.shared.transport.TcpTransport;
import org.junit.Assert;
import org.junit.Test;
@ -14,6 +24,7 @@ import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import java.io.File;
/**
@ -331,4 +342,95 @@ public class TestAppResource extends BaseJerseyTest {
greenMail.stop();
}
/**
* Test the LDAP authentication.
*/
@Test
public void testLdapAuthentication() throws Exception {
// Start LDAP server
final DirectoryServiceFactory factory = new DefaultDirectoryServiceFactory();
factory.init("Test");
final DirectoryService directoryService = factory.getDirectoryService();
directoryService.getChangeLog().setEnabled(false);
directoryService.setShutdownHookEnabled(true);
final Partition partition = new AvlPartition(directoryService.getSchemaManager());
partition.setId("Test");
partition.setSuffixDn(new Dn(directoryService.getSchemaManager(), "o=TEST"));
partition.initialize();
directoryService.addPartition(partition);
final LdapServer ldapServer = new LdapServer();
ldapServer.setTransports(new TcpTransport("localhost", 11389));
ldapServer.setDirectoryService(directoryService);
directoryService.startup();
ldapServer.start();
// Load test data in LDAP
new LdifFileLoader(directoryService.getAdminSession(), new File(Resources.getResource("test.ldif").getFile()), null).execute();
// Login admin
String adminToken = clientUtil.login("admin", "admin", false);
// Get the LDAP configuration
JsonObject json = target().path("/app/config_ldap").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
.get(JsonObject.class);
Assert.assertFalse(json.getBoolean("enabled"));
// Change LDAP configuration
target().path("/app/config_ldap").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
.post(Entity.form(new Form()
.param("enabled", "true")
.param("host", "localhost")
.param("port", "11389")
.param("admin_dn", "uid=admin,ou=system")
.param("admin_password", "secret")
.param("base_dn", "o=TEST")
.param("filter", "(&(objectclass=inetOrgPerson)(uid=USERNAME))")
.param("default_email", "devnull@teedy.io")
.param("default_storage", "100000000")
), JsonObject.class);
// Get the LDAP configuration
json = target().path("/app/config_ldap").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
.get(JsonObject.class);
Assert.assertTrue(json.getBoolean("enabled"));
Assert.assertEquals("localhost", json.getString("host"));
Assert.assertEquals(11389, json.getJsonNumber("port").intValue());
Assert.assertEquals("uid=admin,ou=system", json.getString("admin_dn"));
Assert.assertEquals("secret", json.getString("admin_password"));
Assert.assertEquals("o=TEST", json.getString("base_dn"));
Assert.assertEquals("(&(objectclass=inetOrgPerson)(uid=USERNAME))", json.getString("filter"));
Assert.assertEquals("devnull@teedy.io", json.getString("default_email"));
Assert.assertEquals(100000000L, json.getJsonNumber("default_storage").longValue());
// Login with a LDAP user
String ldapTopen = clientUtil.login("ldap1", "secret", false);
// Check user informations
json = target().path("/user").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, ldapTopen)
.get(JsonObject.class);
Assert.assertEquals("ldap1@teedy.io", json.getString("email"));
// List all documents
json = target().path("/document/list")
.queryParam("sort_column", 3)
.queryParam("asc", true)
.request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, ldapTopen)
.get(JsonObject.class);
JsonArray documents = json.getJsonArray("documents");
Assert.assertEquals(0, documents.size());
// Stop LDAP server
ldapServer.stop();
directoryService.shutdown();
}
}

View File

@ -10,3 +10,4 @@ log4j.logger.com.sismics.util.jpa=ERROR
log4j.logger.org.hibernate=ERROR
log4j.logger.org.apache.pdfbox=INFO
log4j.logger.com.mchange=ERROR
log4j.logger.org.apache.directory=ERROR

View File

@ -0,0 +1,19 @@
version: 1
dn: o=TEST
objectclass: domain
objectclass: top
objectclass: extensibleObject
dc: TEST
o: TEST
dn: uid=ldap1,o=TEST
objectClass: top
objectClass: inetOrgPerson
objectClass: person
objectClass: organizationalPerson
cn: ldap1
sn: LDAP 1
uid: ldap1
userPassword: secret
mail: ldap1@teedy.io

View File

@ -50,6 +50,7 @@
<com.sun.mail.javax.mail.version>1.6.2</com.sun.mail.javax.mail.version>
<org.jsoup.jsoup.version>1.11.3</org.jsoup.jsoup.version>
<com.squareup.okhttp3.okhttp.version>3.11.0</com.squareup.okhttp3.okhttp.version>
<org.apache.directory.server.apacheds-all.version>2.0.0-M17</org.apache.directory.server.apacheds-all.version>
<org.eclipse.jetty.jetty-server.version>9.4.17.v20190418</org.eclipse.jetty.jetty-server.version>
<org.eclipse.jetty.jetty-webapp.version>9.4.17.v20190418</org.eclipse.jetty.jetty-webapp.version>
@ -459,6 +460,12 @@
<version>${org.postgresql.postgresql.version}</version>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-all</artifactId>
<version>${org.apache.directory.server.apacheds-all.version}</version>
</dependency>
<!-- Servlet listener to register SPI ImageIO plugins -->
<dependency>
<groupId>com.twelvemonkeys.servlet</groupId>