Skip to content

Commit

Permalink
🚚 : create specific package for security config
Browse files Browse the repository at this point in the history
  • Loading branch information
cdubuisson committed Oct 16, 2019
1 parent 26e21f4 commit 09e61de
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.codeka.gaia.config;
package io.codeka.gaia.config.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -11,21 +12,19 @@
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Value("${gaia.ldap.userDnPatterns:}")
private String userDnPatterns;

@Value("${gaia.ldap.url:}")
private String url;

@Value("${gaia.ldap.enabled:false}")
private boolean ldapEnabled;

@Value("${gaia.admin-password:admin123}")
private String adminPassword;

private SuccessHandler successHandler;

@Autowired
public SecurityConfig(SuccessHandler successHandler) {
this.successHandler = successHandler;
}

@Bean
PasswordEncoder bcrypt(){
return new BCryptPasswordEncoder();
Expand All @@ -41,7 +40,7 @@ protected void configure(HttpSecurity http) throws Exception {
.antMatchers("/css/**", "/js/**", "/favicon.ico", "/images/**").permitAll()
.antMatchers("/**").authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.formLogin().loginPage("/login").successHandler(successHandler).permitAll()
.and()
.logout().permitAll();
}
Expand All @@ -54,15 +53,5 @@ public void configure(AuthenticationManagerBuilder auth) throws Exception {
.withUser("admin").password(bcrypt().encode(adminPassword)).authorities("ROLE_ADMIN")
.and()
.withUser("user").password(bcrypt().encode("user123")).authorities("ROLE_USER");

// configure ldap auth if needed
if(ldapEnabled){
auth
.ldapAuthentication()
.userDnPatterns(userDnPatterns)
.contextSource()
.url(url);
}
}

}
44 changes: 44 additions & 0 deletions src/main/java/io/codeka/gaia/config/security/SuccessHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.codeka.gaia.config.security;

import io.codeka.gaia.teams.User;
import io.codeka.gaia.teams.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.DefaultSavedRequest;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class SuccessHandler implements AuthenticationSuccessHandler {

protected UserRepository userRepository;

@Autowired
public SuccessHandler(UserRepository userRepository) {
this.userRepository = userRepository;
}

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
if (!userRepository.existsById(authentication.getName())) {
// enter this case especially for people authenticated with ldap
userRepository.save(new User(authentication.getName(), null));
}
redirect(request, response);
}

protected void redirect(HttpServletRequest request, HttpServletResponse response) throws IOException {
var savedRequest = (DefaultSavedRequest) request.getSession().getAttribute("SPRING_SECURITY_SAVED_REQUEST");
if (savedRequest == null) {
// redirect to home page
response.sendRedirect("/");
} else {
// redirect to asked page
response.sendRedirect(savedRequest.getRequestURI());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.codeka.gaia.config;
package io.codeka.gaia.config.security.actuator;

import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Configuration;
Expand All @@ -12,12 +12,13 @@
*/
@Configuration
@Order(50)
public class ActuatorSecurity extends WebSecurityConfigurerAdapter {

public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(EndpointRequest.to("health", "info")).authorizeRequests()
.anyRequest().permitAll();
http
.requestMatcher(EndpointRequest.to("health", "info"))
.authorizeRequests()
.anyRequest()
.permitAll();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.codeka.gaia.config.security.ldap;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@ConditionalOnProperty(prefix = "gaia", name = "ldap.enabled", havingValue = "true")
@Order(60)
public class LdapSecurityConfig extends WebSecurityConfigurerAdapter {

@Value("${gaia.ldap.userDnPatterns}")
private String userDnPatterns;

@Value("${gaia.ldap.url}")
private String url;

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userDnPatterns(userDnPatterns)
.contextSource()
.url(url);
}
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
package io.codeka.gaia.config;
package io.codeka.gaia.config.security;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.stereotype.Controller;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.MockMvc;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;

@SpringBootTest(classes = {EmptyController.class, SecurityConfig.class})
@SpringBootTest(classes = SecurityConfig.class)
@DirtiesContext
@AutoConfigureMockMvc
@TestPropertySource(properties = "gaia.admin-password=admin456")
class SecurityConfigTest {
class SecurityConfigIT {

@Autowired
private MockMvc mockMvc;

@MockBean
SuccessHandler successHandler;

@Test
void adminUserPassword_shouldBeConfigurable() throws Exception {
mockMvc.perform(formLogin().user("admin").password("admin456"))
.andExpect(authenticated().withUsername("admin"));
}

}

@Controller
class EmptyController{}
verify(successHandler).onAuthenticationSuccess(any(), any(), any());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package io.codeka.gaia.config.security;

import io.codeka.gaia.teams.User;
import io.codeka.gaia.teams.repository.UserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.savedrequest.DefaultSavedRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class SuccessHandlerTest {

@Mock
UserRepository userRepository;

@Mock
HttpServletRequest request;

@Mock
HttpServletResponse response;

@Mock
Authentication authentication;

@Mock
HttpSession httpSession;

@InjectMocks
SuccessHandler successHandler;

@BeforeEach
void setup() {
when(authentication.getName()).thenReturn("spencer");
when(request.getSession()).thenReturn(httpSession);
}

@Test
void onAuthenticationSuccess_shouldCreateUser_whenNotExists() throws IOException {
// when
when(userRepository.existsById(anyString())).thenReturn(false);
successHandler.onAuthenticationSuccess(request, response, authentication);

// then
var captor = ArgumentCaptor.forClass(User.class);
verify(userRepository).save(captor.capture());
assertThat(captor.getValue()).isNotNull()
.hasFieldOrPropertyWithValue("username", "spencer")
.hasFieldOrPropertyWithValue("team", null);
}

@Test
void onAuthenticationSuccess_shouldNotCreateUser_whenExists() throws IOException {
// when
when(userRepository.existsById(anyString())).thenReturn(true);
successHandler.onAuthenticationSuccess(request, response, authentication);

// then
verify(userRepository, never()).save(any(User.class));
}

@Test
void onAuthenticationSuccess_shouldRedirectToHomePage() throws IOException {
// when
when(userRepository.existsById(anyString())).thenReturn(false);
successHandler.onAuthenticationSuccess(request, response, authentication);

// then
verify(response).sendRedirect("/");
}

@Test
void onAuthenticationSuccess_shouldRedirectToAskedPage_whenSpecified() throws IOException {
// given
var savedRequest = mock(DefaultSavedRequest.class);

// when
when(userRepository.existsById(anyString())).thenReturn(false);
when(httpSession.getAttribute("SPRING_SECURITY_SAVED_REQUEST")).thenReturn(savedRequest);
when(savedRequest.getRequestURI()).thenReturn("/test_url");
successHandler.onAuthenticationSuccess(request, response, authentication);

// then
verify(response).sendRedirect("/test_url");
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.codeka.gaia.config;
package io.codeka.gaia.config.security.actuator;

import io.codeka.gaia.test.MongoContainer;
import org.junit.jupiter.api.Test;
Expand All @@ -20,7 +20,7 @@
@AutoConfigureMockMvc
@DirtiesContext
@Testcontainers
class ActuatorSecurityTest {
class ActuatorSecurityConfigIT {

@Autowired
private MockMvc mockMvc;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.codeka.gaia.config.security.ldap;

import io.codeka.gaia.test.MongoContainer;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;

@DirtiesContext
@Testcontainers
class LdapSecurityConfigIT {

@Container
private static MongoContainer mongoContainer = new MongoContainer();

@Nested
@SpringBootTest(properties = "gaia.ldap.enabled=false")
class LdapSecurityConfigNotLoadedTest {
@Test
void ldapSecurityConfig_shouldNotBeInstantiated(
@Autowired(required = false) LdapSecurityConfig ldapSecurityConfig) {
assertNull(ldapSecurityConfig);
}
}

@Nested
@SpringBootTest(properties = {
"gaia.ldap.enabled=true",
"gaia.ldap.userDnPatterns=test_dn",
"gaia.ldap.url=ldap://test_url",
})
class LdapSecurityConfigLoadedTest {
@Test
void ldapSecurityConfig_shouldBeInstantiated(
@Autowired(required = false) LdapSecurityConfig ldapSecurityConfig) {
assertNotNull(ldapSecurityConfig);
}
}
}

0 comments on commit 09e61de

Please sign in to comment.