Commit 79e53b68 authored by Piotr Gawron's avatar Piotr Gawron
Browse files

integration tests of ldap

parent f4ad25ea
package lcsb.mapviewer.web.config;
import com.unboundid.ldap.sdk.LDAPException;
import lcsb.mapviewer.model.user.User;
import lcsb.mapviewer.services.UserDTO;
import lcsb.mapviewer.services.interfaces.ILdapService;
import lcsb.mapviewer.services.interfaces.IUserService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.unboundid.ldap.sdk.LDAPException;
import lcsb.mapviewer.model.user.User;
import lcsb.mapviewer.services.UserDTO;
import lcsb.mapviewer.services.interfaces.ILdapService;
import lcsb.mapviewer.services.interfaces.IUserService;
@Order(2)
@Service
public class LdapAuthenticationProvider implements AuthenticationProvider {
Logger logger = LogManager.getLogger();
private IUserService userService;
private ILdapService ldapService;
private UserDetailsService userDetailsService;
@Autowired
public LdapAuthenticationProvider(IUserService userService,
ILdapService ldapService,
UserDetailsService userDetailsService) {
ILdapService ldapService,
UserDetailsService userDetailsService) {
this.userService = userService;
this.ldapService = ldapService;
this.userDetailsService = userDetailsService;
......@@ -40,6 +42,13 @@ public class LdapAuthenticationProvider implements AuthenticationProvider {
if (username.isEmpty()) {
throw new BadCredentialsException("Invalid username.");
}
User existingUser = userService.getUserByLogin(username);
if (existingUser!=null) {
if (!existingUser.isConnectedToLdap()) {
throw new BadCredentialsException("User cannot authenticate over LDAP");
}
}
boolean ldapLoginSuccess;
try {
......@@ -52,16 +61,15 @@ public class LdapAuthenticationProvider implements AuthenticationProvider {
throw new BadCredentialsException("Invalid credentials or username.");
}
boolean userExistsLocally = userService.getUserByLogin(username) != null;
boolean userExistsLocally = existingUser != null;
if (!userExistsLocally) {
createLocalUser(authentication);
}
return new UsernamePasswordAuthenticationToken(
username,
authentication.getCredentials(),
userDetailsService.loadUserByUsername(username).getAuthorities()
);
username,
authentication.getCredentials(),
userDetailsService.loadUserByUsername(username).getAuthorities());
}
@Override
......@@ -82,6 +90,9 @@ public class LdapAuthenticationProvider implements AuthenticationProvider {
newUser.setName(userDTO.getFirstName());
newUser.setSurname(userDTO.getLastName());
newUser.setEmail(userDTO.getEmail());
// spring requires not null password - the password is hashed and the hash would
// never be equal to empty string
newUser.setCryptedPassword("");
userService.addUser(newUser);
userService.grantDefaultPrivileges(newUser);
}
......
......@@ -50,7 +50,7 @@ public class LocalAuthenticationProvider implements AuthenticationProvider {
}
User user = userService.getUserByLogin(username);
if (user == null || user.isConnectedToLdap()) {
throw new InternalAuthenticationServiceException("Provider cannot authenticate user.");
throw new UsernameNotFoundException("Provider cannot authenticate user.");
}
return daoAuthenticationProvider.authenticate(authentication);
}
......
package lcsb.mapviewer.web;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import lcsb.mapviewer.model.user.ConfigurationElementType;
import lcsb.mapviewer.services.impl.LdapService;
import lcsb.mapviewer.services.interfaces.IConfigurationService;
import lcsb.mapviewer.services.interfaces.ILdapService;
@Profile("ldapTest")
@Configuration
public class LdapServiceTestConfiguration {
@Autowired
IConfigurationService configurationService;
Logger logger = LogManager.getLogger();
static String TEST_LOGIN = "john.doe.test";
static String TEST_PASSWD = "test_passwd";
static String LDAP_FILE_CONTENT = "./src/test/resources/ldap/john-doe-test-example.ldif";
static String TEST_INVALID_PASSWD = "incorrect password";
@Bean
@Primary
public ILdapService createMockLdapService() throws LDAPException {
configurationService.setConfigurationValue(ConfigurationElementType.LDAP_BASE_DN, "dc=uni,dc=lu");
configurationService.setConfigurationValue(ConfigurationElementType.LDAP_OBJECT_CLASS, "person");
configurationService.setConfigurationValue(ConfigurationElementType.LDAP_FILTER,
"memberof=cn=gitlab,cn=groups,cn=accounts,dc=uni,dc=lu");
LdapService ldapService = Mockito.spy(new LdapService(null));
ldapService.setConfigurationService(configurationService);
Mockito.doAnswer(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=" + TEST_LOGIN + ",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, LDAP_FILE_CONTENT);
ds.startListening();
return ds.getConnection();
}
}).when(ldapService).getConnection();
return ldapService;
}
}
......@@ -8,29 +8,22 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.*;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.transaction.annotation.Transactional;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import lcsb.mapviewer.model.user.ConfigurationElementType;
import lcsb.mapviewer.model.user.User;
import lcsb.mapviewer.services.impl.LdapService;
import lcsb.mapviewer.services.interfaces.*;
import lcsb.mapviewer.services.interfaces.IUserService;
import lcsb.mapviewer.web.config.SpringWebConfig;
@ActiveProfiles("ldapTest")
@RunWith(SpringJUnit4ClassRunner.class)
@Transactional
@Rollback
......@@ -38,70 +31,31 @@ import lcsb.mapviewer.web.config.SpringWebConfig;
@ContextConfiguration(classes = SpringWebConfig.class)
public class SpringSecurityLdapIntegrationTest extends ControllerIntegrationTest {
private static final String LOCAL_PASSWORD = "xxx";
static Logger logger = LogManager.getLogger(SpringSecurityLdapIntegrationTest.class);
@Autowired
private IUserService userService;
@Autowired
private UserDetailsService customUserDetailsService;
private static String TEST_LOGIN = "john.doe.test";
private static String TEST_PASSWD = "test_passwd";
private static String TEST_INVALID_PASSWD = "incorrect password";
@Autowired
ILdapService originalLdapService;
@Autowired
IConfigurationService configurationService;
@Before
public void setUp() throws LDAPException {
LdapService mockLdapService = createMockLdapService("./src/test/resources/ldap/john-doe-test-example.ldif", TEST_LOGIN,
TEST_PASSWD);
// TODO use this mock for the LDAP connection
}
@After
public void tearDown() {
}
private LdapService createMockLdapService(String filename, String login, String passwd) throws LDAPException {
configurationService.setConfigurationValue(ConfigurationElementType.LDAP_BASE_DN, "dc=uni,dc=lu");
configurationService.setConfigurationValue(ConfigurationElementType.LDAP_OBJECT_CLASS, "person");
configurationService.setConfigurationValue(ConfigurationElementType.LDAP_FILTER,
"memberof=cn=gitlab,cn=groups,cn=accounts,dc=uni,dc=lu");
LdapService ldapService = Mockito.spy(new LdapService(null));
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=" + login + ",cn=users,cn=accounts,dc=uni,dc=lu", 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, filename);
ds.startListening();
return ds.getConnection();
}
});
return ldapService;
}
@Test
public void testInvalidLoginFromLdap() throws Exception {
int count = userService.getUsers().size();
RequestBuilder request = post("/api/doLogin")
.param("login", TEST_LOGIN)
.param("password", TEST_INVALID_PASSWD);
.param("login", LdapServiceTestConfiguration.TEST_LOGIN)
.param("password", LdapServiceTestConfiguration.TEST_INVALID_PASSWD);
mockMvc.perform(request)
.andExpect(status().is4xxClientError());
......@@ -112,12 +66,12 @@ public class SpringSecurityLdapIntegrationTest extends ControllerIntegrationTest
@Test
public void testLoginFromLdap() throws Exception {
RequestBuilder request = post("/api/doLogin")
.param("login", TEST_LOGIN)
.param("password", TEST_PASSWD);
.param("login", LdapServiceTestConfiguration.TEST_LOGIN)
.param("password", LdapServiceTestConfiguration.TEST_PASSWD);
mockMvc.perform(request)
.andExpect(status().is2xxSuccessful());
User user = userService.getUserByLogin(TEST_LOGIN);
User user = userService.getUserByLogin(LdapServiceTestConfiguration.TEST_LOGIN);
assertNotNull("After authentication from LDAP user is not present in the system", user);
assertTrue("LDAP user password should be empty",
user.getCryptedPassword() == null || user.getCryptedPassword().isEmpty());
......@@ -129,25 +83,56 @@ public class SpringSecurityLdapIntegrationTest extends ControllerIntegrationTest
int count = userService.getUsers().size();
RequestBuilder request = post("/api/doLogin")
.param("login", TEST_LOGIN)
.param("password", TEST_PASSWD);
.param("login", LdapServiceTestConfiguration.TEST_LOGIN)
.param("password", LdapServiceTestConfiguration.TEST_PASSWD);
mockMvc.perform(request)
.andExpect(status().is2xxSuccessful());
request = post("/api/doLogin")
.param("login", TEST_LOGIN.toLowerCase())
.param("password", TEST_PASSWD);
.param("login", LdapServiceTestConfiguration.TEST_LOGIN.toLowerCase())
.param("password", LdapServiceTestConfiguration.TEST_PASSWD);
mockMvc.perform(request)
.andExpect(status().is2xxSuccessful());
request = post("/api/doLogin")
.param("login", TEST_LOGIN.toUpperCase())
.param("password", TEST_PASSWD);
.param("login", LdapServiceTestConfiguration.TEST_LOGIN.toUpperCase())
.param("password", LdapServiceTestConfiguration.TEST_PASSWD);
mockMvc.perform(request)
.andExpect(status().is2xxSuccessful());
assertEquals("LDAP login is case insensitive and no new user should be added for different cases",
count + 1, userService.getUsers().size());
}
@Test
public void testLocalAccountShouldntAuthenticateFromLdap() throws Exception {
createUser(LdapServiceTestConfiguration.TEST_LOGIN, LOCAL_PASSWORD);
RequestBuilder request = post("/api/doLogin")
.param("login", LdapServiceTestConfiguration.TEST_LOGIN)
.param("password", LdapServiceTestConfiguration.TEST_PASSWD);
mockMvc.perform(request)
.andExpect(status().is4xxClientError());
}
@Test
public void testLdapAccountShouldntAuthenticateFromLocal() throws Exception {
User user = createUser(LdapServiceTestConfiguration.TEST_LOGIN, LOCAL_PASSWORD);
user.setConnectedToLdap(true);
userService.updateUser(user);
RequestBuilder request = post("/api/doLogin")
.param("login", LdapServiceTestConfiguration.TEST_LOGIN)
.param("password", LdapServiceTestConfiguration.TEST_PASSWD);
mockMvc.perform(request)
.andExpect(status().is2xxSuccessful());
request = post("/api/doLogin")
.param("login", LdapServiceTestConfiguration.TEST_LOGIN)
.param("password", LOCAL_PASSWORD);
mockMvc.perform(request)
.andExpect(status().is4xxClientError());
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment