Commit 8d0dc7af authored by Piotr Gawron's avatar Piotr Gawron
Browse files

ldap connector implemented

parent 7c40dabc
......@@ -7,6 +7,7 @@ public enum ConfigurationElementTypeGroup {
OVERLAYS("Overlays"), //
POINT_AND_CLICK("Point and click"), //
SERVER_CONFIGURATION("Server configuration"),//
LDAP_CONFIGURATION("LDAP configuration"),//
;
private String commonName;
......
......@@ -82,6 +82,8 @@
<cglib.version>2.2.2</cglib.version>
<unboundid-ldapsdk.version>4.0.6</unboundid-ldapsdk.version>
<mockito.version>1.10.19</mockito.version>
</properties>
......
......@@ -118,6 +118,13 @@
<version>3.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.unboundid/unboundid-ldapsdk -->
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
<version>${unboundid-ldapsdk.version}</version>
</dependency>
<!-- mockito used for testing -->
<dependency>
<groupId>org.mockito</groupId>
......
package lcsb.mapviewer.services;
public class UserDTO {
private String login;
private String firstName;
private String lastName;
private String email;
private String bindDn;
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getBindDn() {
return bindDn;
}
public void setBindDn(String bindDn) {
this.bindDn = bindDn;
}
}
package lcsb.mapviewer.services.impl;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import com.unboundid.ldap.sdk.Attribute;
import com.unboundid.ldap.sdk.BindResult;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchResultEntry;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.ldap.sdk.SimpleBindRequest;
import com.unboundid.util.ssl.SSLUtil;
import com.unboundid.util.ssl.TrustAllTrustManager;
import lcsb.mapviewer.common.exception.InvalidStateException;
import lcsb.mapviewer.model.user.ConfigurationElementType;
import lcsb.mapviewer.services.UserDTO;
import lcsb.mapviewer.services.interfaces.IConfigurationService;
import lcsb.mapviewer.services.interfaces.ILdapService;
@Transactional(value = "txManager")
public class LdapService implements ILdapService {
Logger logger = Logger.getLogger(LdapService.class);
@Autowired
private IConfigurationService configurationService;
protected LDAPConnection getConnection() throws LDAPException {
String address = configurationService.getConfigurationValue(ConfigurationElementType.LDAP_ADDRESS);
if (address == null || address.trim().isEmpty()) {
return null;
}
boolean ssl = "true"
.equalsIgnoreCase(configurationService.getConfigurationValue(ConfigurationElementType.LDAP_SSL));
String portString = configurationService.getConfigurationValue(ConfigurationElementType.LDAP_PORT);
if (portString == null || address.trim().isEmpty()) {
if (ssl) {
portString = "636";
} else {
portString = "389";
}
}
int port = Integer.parseInt(portString);
LDAPConnection connection;
if (ssl) {
SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
try {
connection = new LDAPConnection(sslUtil.createSSLSocketFactory());
} catch (GeneralSecurityException e) {
throw new InvalidStateException(e);
}
} else {
connection = new LDAPConnection();
}
connection.connect(address, port);
String bindDn = configurationService.getConfigurationValue(ConfigurationElementType.LDAP_BIND_DN);
String password = configurationService.getConfigurationValue(ConfigurationElementType.LDAP_PASSWORD);
if (bindDn == null || bindDn.trim().isEmpty()) {
connection.bind(new SimpleBindRequest());
} else {
connection.bind(bindDn, password);
}
return connection;
}
@Override
public boolean login(String login, String password) throws LDAPException {
if (!isValidConfiguratio()) {
logger.warn("Invalid LDAP configuration");
return false;
}
LDAPConnection connection = getConnection();
UserDTO user = getUserByLogin(login);
if (user != null) {
try {
BindResult result = connection.bind(user.getBindDn(), password);
return result.getResultCode().equals(ResultCode.SUCCESS);
} catch (Exception e) {
return false;
}
}
return false;
}
@Override
public List<String> getUsernames() throws LDAPException {
if (!isValidConfiguratio()) {
logger.warn("Invalid LDAP configuration");
return new ArrayList<>();
}
List<String> result = new ArrayList<>();
LDAPConnection connection = getConnection();
Filter f2 = createObjectClassFilter();
Filter f3 = createAttributeFilter();
Filter filter = Filter.createANDFilter(f2, f3);
String baseDn = configurationService.getConfigurationValue(ConfigurationElementType.LDAP_BASE_DN);
SearchResult searchResult = connection.search(baseDn, SearchScope.SUB, filter);
for (SearchResultEntry entry : searchResult.getSearchEntries()) {
Attribute uid = entry.getAttribute("uid");
if (uid != null) {
result.add(uid.getValue());
} else {
logger.warn("Invalid ldap entry: " + entry);
}
}
connection.close();
return result;
}
@Override
public UserDTO getUserByLogin(String login) throws LDAPException {
if (!isValidConfiguratio()) {
logger.warn("Invalid LDAP configuration");
return null;
}
LDAPConnection connection = getConnection();
try {
String baseDn = configurationService.getConfigurationValue(ConfigurationElementType.LDAP_BASE_DN);
String firstNameAttribute = configurationService
.getConfigurationValue(ConfigurationElementType.LDAP_FIRST_NAME_ATTRIBUTE);
String lastNameAttribute = configurationService
.getConfigurationValue(ConfigurationElementType.LDAP_LAST_NAME_ATTRIBUTE);
String emailAttribute = configurationService.getConfigurationValue(ConfigurationElementType.LDAP_EMAIL_ATTRIBUTE);
Filter f1 = createLoginFilter(login);
Filter f2 = createObjectClassFilter();
Filter f3 = createAttributeFilter();
Filter filter = Filter.createANDFilter(f1, f2, f3);
SearchResult searchResult = connection.search(baseDn, SearchScope.SUB, filter);
for (SearchResultEntry entry : searchResult.getSearchEntries()) {
UserDTO result = new UserDTO();
result.setBindDn(entry.getDN());
Attribute uidAttribute = entry.getAttribute("uid");
if (uidAttribute != null) {
result.setLogin(uidAttribute.getValue());
} else {
logger.warn("Invalid ldap entry: " + entry);
}
if (!firstNameAttribute.trim().isEmpty()) {
Attribute firstName = entry.getAttribute(firstNameAttribute);
if (firstName != null) {
result.setFirstName(firstName.getValue());
}
}
if (!lastNameAttribute.trim().isEmpty()) {
Attribute lastName = entry.getAttribute(lastNameAttribute);
if (lastName != null) {
result.setLastName(lastName.getValue());
}
}
if (!emailAttribute.trim().isEmpty()) {
Attribute emailName = entry.getAttribute(emailAttribute);
if (emailName != null) {
result.setEmail(emailName.getValue());
}
}
return result;
}
return null;
} finally {
connection.close();
}
}
private Filter createObjectClassFilter() {
String objectClass = configurationService.getConfigurationValue(ConfigurationElementType.LDAP_OBJECT_CLASS);
if (objectClass == null || objectClass.trim().isEmpty()) {
objectClass = "*";
}
return Filter.createEqualityFilter("objectClass", objectClass);
}
private Filter createAttributeFilter() throws LDAPException {
String ldapStringFilter = configurationService.getConfigurationValue(ConfigurationElementType.LDAP_FILTER);
if (ldapStringFilter == null || ldapStringFilter.trim().isEmpty()) {
return Filter.create("");
}
return Filter.create(ldapStringFilter);
}
private Filter createLoginFilter(String login) {
return Filter.createEqualityFilter("uid", login);
}
public IConfigurationService getConfigurationService() {
return configurationService;
}
public void setConfigurationService(IConfigurationService configurationService) {
this.configurationService = configurationService;
}
@Override
public boolean isValidConfiguratio() {
try {
String baseDn = configurationService.getConfigurationValue(ConfigurationElementType.LDAP_BASE_DN);
if (baseDn == null || baseDn.trim().isEmpty()) {
return false;
}
LDAPConnection connection = getConnection();
if (connection != null) {
connection.close();
return true;
}
return false;
} catch (Exception e) {
logger.error(e, e);
return false;
}
}
}
package lcsb.mapviewer.services.interfaces;
import java.util.List;
import com.unboundid.ldap.sdk.LDAPException;
import lcsb.mapviewer.model.user.ConfigurationElementTypeGroup;
import lcsb.mapviewer.services.UserDTO;
/**
* Connection service to LDAP server.
*
* @author Piotr Gawron
*
*/
public interface ILdapService {
/**
* Checks if login and password match
*
* @param login
* user login
* @param password
* password
* @return true if user login/password match
* @throws LDAPException
* thrown when there is problem with LDAP connection
*/
boolean login(String login, String password) throws LDAPException;
/**
* Returns list of user names available in the LDAP server.
*
* @return list of user names
* @throws LDAPException
* thrown when there is problem with LDAP connection
*/
List<String> getUsernames() throws LDAPException;
/**
* Returns user data information from LDAP for given login.
*
* @param login
* user for which we obtain data
* @return user data information from LDAP for given login or null if such user
* doesn't exist
* @throws LDAPException
* thrown when there is problem with LDAP connection
*/
UserDTO getUserByLogin(String login) throws LDAPException;
/**
* Checks if LDAP configuration
* ({@link ConfigurationElementTypeGroup#LDAP_CONFIGURATION}) is valid.
*
* @return true if LDAP configuration
* ({@link ConfigurationElementTypeGroup#LDAP_CONFIGURATION}) is valid
*/
boolean isValidConfiguratio();
}
......@@ -23,6 +23,8 @@
<bean id="LayoutService" class="lcsb.mapviewer.services.impl.LayoutService"/>
<bean id="LdapService" class="lcsb.mapviewer.services.impl.LdapService"/>
<bean id="LogService" class="lcsb.mapviewer.services.impl.LogService"/>
<bean id="MiriamService" class="lcsb.mapviewer.services.impl.MiriamService"/>
......
package lcsb.mapviewer.services.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.List;
import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.test.annotation.Rollback;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.sdk.LDAPConnection;
import lcsb.mapviewer.model.user.ConfigurationElementType;
import lcsb.mapviewer.services.ServiceTestFunctions;
import lcsb.mapviewer.services.UserDTO;
@Rollback(true)
public class LdapServiceTest extends ServiceTestFunctions {
static Logger logger = Logger.getLogger(LdapServiceTest.class);
LdapService ldapService;
@Before
public void setUp() throws Exception {
try {
configurationService.setConfigurationValue(ConfigurationElementType.LDAP_BASE_DN, "dc=uni,dc=lu");
configurationService.setConfigurationValue(ConfigurationElementType.LDAP_OBJECT_CLASS, "person");
ldapService = Mockito.spy(LdapService.class);
ldapService.setConfigurationService(configurationService);
Mockito.when(ldapService.getConnection()).thenAnswer(new Answer<LDAPConnection>() {
@Override
public LDAPConnection answer(InvocationOnMock invocation) throws Throwable {
// Create the configuration to use for the server.
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=uni,dc=lu");
config.addAdditionalBindCredentials("uid=piotr.gawron,cn=users,cn=accounts,dc=uni,dc=lu", "test_passwd");
config.setSchema(null);
// Create the directory server instance, populate it with data from the
// "test-data.ldif" file, and start listening for client connections.
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
ds.importFromLDIF(true, "testFiles/ldap/testdata.ldif");
ds.startListening();
return ds.getConnection();
}
});
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@After
public void tearDown() throws Exception {
}
@Test
public void testIsValidConfiguration() throws Exception {
try {
assertTrue(ldapService.isValidConfiguratio());
configurationService.setConfigurationValue(ConfigurationElementType.LDAP_BASE_DN, "");
assertFalse(ldapService.isValidConfiguratio());
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testLogin() throws Exception {
try {
assertTrue(ldapService.login("piotr.gawron", "test_passwd"));
assertFalse(ldapService.login("piotr.gawron", "invalid_password"));
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testGetUsernames() throws Exception {
try {
List<String> list = ldapService.getUsernames();
assertEquals(2, list.size());
assertTrue(list.contains("piotr.gawron"));
assertFalse(list.contains("john.doe"));
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testGetUsernamesWithFiltering() throws Exception {
try {
configurationService.setConfigurationValue(ConfigurationElementType.LDAP_FILTER,
"(memberof=cn=owncloud,cn=groups,cn=accounts,dc=uni,dc=lu)");
List<String> list = ldapService.getUsernames();
assertEquals(1, list.size());
assertTrue(list.contains("piotr.gawron"));
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@Test
public void testGetUserByLogin() throws Exception {
try {
UserDTO user = ldapService.getUserByLogin("piotr.gawron");
assertEquals("Piotr", user.getFirstName());
assertEquals("Gawron", user.getLastName());
assertEquals("piotr.gawron", user.getLogin());
assertEquals("piotr.gawron@uni.lu", user.getEmail());
assertNotNull(user.getBindDn());
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
version: 1
dn: dc=uni,dc=lu
objectClass: top
objectClass: domain
dc: uni
dn: cn=accounts,dc=uni,dc=lu
objectClass: extensibleObject
cn: accounts
dn: cn=users,cn=accounts,dc=uni,dc=lu
objectClass: extensibleObject
cn: users
dn: uid=piotr.gawron,cn=users,cn=accounts,dc=uni,dc=lu
objectClass: mepOriginEntry
objectClass: ipaSshGroupOfPubKeys
objectClass: posixaccount
objectClass: inetuser
objectClass: krbprincipalaux
objectClass: krbticketpolicyaux
objectClass: organizationalperson
objectClass: inetorgperson
objectClass: ipasshuser
objectClass: top
objectClass: person
objectClass: ipaobject
cn: Piotr Gawron
gidNumber: 369550501
homeDirectory: /home/piotr.gawron
ipaUniqueID: adf723e6-20e4-11e5-8907-001a4ae51219
sn: Gawron
uid: piotr.gawron
uidNumber: 369550501
displayName: Piotr Gawron
gecos: Piotr Gawron
givenName: Piotr
initials: PG
krbLastPwdChange: 20180608111630Z
krbPasswordExpiration: 20190223111630Z
krbPrincipalName: piotr.gawron@UNI.LU
loginShell: /bin/bash
mail: piotr.gawron@uni.lu
memberOf: cn=lcsb,cn=groups,cn=accounts,dc=uni,dc=lu
memberOf: ipaUniqueID=d7549fa2-03a2-11e5-95f9-00163e0a4f7b,cn=hbac,dc=uni,dc
=lu
memberOf: ipaUniqueID=eff7677e-03a2-11e5-add5-00163e0a4f7b,cn=sudorules,cn=s
udo,dc=uni,dc=lu
memberOf: ipaUniqueID=f10ec0f6-7ef6-11e5-957b-001a4ae5121e,cn=hbac,dc=uni,dc
=lu
memberOf: ipaUniqueID=2cf9b59e-7ef7-11e5-89c0-001a4ae5121e,cn=sudorules,cn=s
udo,dc=uni,dc=lu
memberOf: ipaUniqueID=eeb5f68e-9775-11e5-81fb-00163e0a4f7b,cn=hbac,dc=uni,dc
=lu
memberOf: ipaUniqueID=176f7fb4-9776-11e5-a097-00163e0a4f7b,cn=sudorules,cn=s
udo,dc=uni,dc=lu
memberOf: ipaUniqueID=7dda82ae-99e1-11e5-834b-001a4ae5121e,cn=hbac,dc=uni,dc