package egovframework.rte.fdl.security; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.lang.reflect.Method; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import egovframework.rte.fdl.security.userdetails.EgovUserDetailsVO; import egovframework.rte.fdl.security.userdetails.util.EgovUserDetailsHelper; import egovframework.rte.fdl.security.web.CategoryController; import javax.annotation.Resource; import javax.servlet.Filter; import javax.sql.DataSource; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.SecurityConfig; import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.config.BeanIds; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.jdbc.JdbcTestUtils; /** * @author sjyoon * */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath*:META-INF/spring/context-common.xml", "classpath*:META-INF/spring/context-datasource-jdbc.xml" }) public class EgovSecurityConfigTest { private static final Logger LOGGER = LoggerFactory.getLogger(EgovSecurityConfigTest.class); @Autowired private ApplicationContext context; @Resource(name = "dataSource") private DataSource dataSource; private boolean isHsql = true; @Resource(name = "jdbcProperties") private Properties jdbcProperties; @Before public void onSetUp() throws Exception { LOGGER.debug("###### EgovSecurityConfigTest.onSetUp START ######"); isHsql = "hsql".equals(jdbcProperties.getProperty("usingDBMS")); if (isHsql) { JdbcTestUtils.executeSqlScript(new JdbcTemplate(dataSource), new ClassPathResource("META-INF/testdata/sample_schema_hsql.sql"), true); } // 테이블 생성 후 테스트를 위하여 여기서 처리 context = new ClassPathXmlApplicationContext( new String[] { "classpath*:META-INF/spring/context-common.xml", "classpath*:META-INF/spring/context-datasource-jdbc.xml", "classpath*:META-INF/spring/config-context.xml", }); LOGGER.debug("###### EgovSecurityConfigTest.onSetUp END ######"); } @After public void onTearDown() throws Exception { LOGGER.debug("###### EgovSecurityConfigTest.onTearDown START ######"); isHsql = "hsql".equals(jdbcProperties.getProperty("usingDBMS")); if (isHsql) { JdbcTestUtils.executeSqlScript(new JdbcTemplate(dataSource), new ClassPathResource("META-INF/testdata/sample_schema_hsql_drop.sql"), true); } LOGGER.debug("###### EgovSecurityConfigTest.onTearDown END ######"); SecurityContextHolder.clearContext(); } private <T extends Filter> T getSecurityFilter(Class<T> type) { Map<String, DefaultSecurityFilterChain> filterChainMap = context.getBeansOfType(DefaultSecurityFilterChain.class); for (DefaultSecurityFilterChain filterChain : filterChainMap.values()) { for (Filter filter : filterChain.getFilters()) { if (type.isInstance(filter)) { return type.cast(filter); } } } throw new NoSuchBeanDefinitionException("No bean of type [" + type.getName() + "] is defined."); } @Test public void testBeanList() { String[] list = context.getBeanDefinitionNames(); for (String bean : list) { System.out.println("===> " + bean + " : " + context.getBean(bean).getClass()); if (context.getBean(bean) instanceof DefaultSecurityFilterChain) { DefaultSecurityFilterChain filterChain = (DefaultSecurityFilterChain) context.getBean(bean); List<Filter> filters = filterChain.getFilters(); for (Filter filter : filters) { System.out.println("======> " + filter + " : "); } } } } @Test public void testGetSecurityFilter() { LogoutFilter logout = getSecurityFilter(LogoutFilter.class); assertNotNull(logout); } /** * DB에 사용자 정보(id/password)를 유지하여 인증처리 함. * DB에 등록된 사용자의 인증 확인 테스트 * @throws Exception */ @Test public void testAllowAccessForAuthorizedUser() throws Exception { UsernamePasswordAuthenticationToken login = new UsernamePasswordAuthenticationToken("jimi", "jimi"); AuthenticationManager authManager = (AuthenticationManager) context.getBean(BeanIds.AUTHENTICATION_MANAGER); SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(login)); LOGGER.debug("### jimi's password is right!!"); /////////// login = new UsernamePasswordAuthenticationToken("test", "test"); authManager = (AuthenticationManager) context.getBean(BeanIds.AUTHENTICATION_MANAGER); SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(login)); LOGGER.debug("### test's password is right!!"); /////////// login = new UsernamePasswordAuthenticationToken("user", "user"); authManager = (AuthenticationManager) context.getBean(BeanIds.AUTHENTICATION_MANAGER); SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(login)); LOGGER.debug("### user's password is right!!"); /////////// login = new UsernamePasswordAuthenticationToken("buyer", "buyer"); authManager = (AuthenticationManager) context.getBean(BeanIds.AUTHENTICATION_MANAGER); SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(login)); LOGGER.debug("### buyer's password is right!!"); } /** * DB에 등록된 사용자의 인증 실패 테스트 * @throws Exception */ @Test(expected=BadCredentialsException.class) public void testRejectAccessForUnauthorizedUser() throws Exception { UsernamePasswordAuthenticationToken login = new UsernamePasswordAuthenticationToken("jimi", "wrongpw"); AuthenticationManager authManager = (AuthenticationManager) context.getBean(BeanIds.AUTHENTICATION_MANAGER); LOGGER.debug("### jimi's password is wrong!!"); SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(login)); } /** * 메소드 수행이 허용된 메소드 실행 시 성공 테스트 * @throws Exception */ @Test public void testMethodAndRoleMapping() throws Exception { DelegatingMethodSecurityMetadataSource definitionsource = (DelegatingMethodSecurityMetadataSource) context.getBean("delegatingMethodSecurityMetadataSource"); Method method = null; Collection<ConfigAttribute> role = null; // test1 : matched role try { method = CategoryController.class.getMethod("selectCategoryList", (Class<?>[])null); } catch (NoSuchMethodException nsme) { LOGGER.error("## testMethodAndRoleMapping : {}", nsme); } role = definitionsource.getAttributes(method, CategoryController.class); assertEquals("ROLE_USER", role.toArray()[0].toString()); LOGGER.debug("## testMethodAndRoleMapping : {} is {}", method.getName(), role.toArray()[0].toString()); } /** * 메소드 수행이 허용되지 않은 메소드 실행 시 실패 테스트 * @throws Exception */ @Test public void testFailedMethodAndRoleMapping() throws Exception { DelegatingMethodSecurityMetadataSource definitionsource = (DelegatingMethodSecurityMetadataSource) context.getBean("delegatingMethodSecurityMetadataSource"); Method method = null; Collection<ConfigAttribute> role = null; // test1 : no matched role try { method = CategoryController.class.getMethod("addCategoryView", (Class<?>[])null); } catch (NoSuchMethodException nsme) { LOGGER.error("## testMethodAndRoleMapping : {}", nsme); } role = definitionsource.getAttributes(method, CategoryController.class); assertEquals(0, role.size()); LOGGER.debug("## testMethodAndRoleMapping : {} is no roles", method.getName()); } /** * 웹 URL 접근 제어 권한에 따른 Role 맵핑을 처리함. * 웹 접근이 허용된 URL로 접근 시 성공 테스트 * @throws Exception */ @Test public void testURLAndRoleMapping() throws Exception { FilterSecurityInterceptor interceptor = (FilterSecurityInterceptor) context.getBean("filterSecurityInterceptor"); FilterInvocationSecurityMetadataSource definitionsource = interceptor.getSecurityMetadataSource(); // "/test.do" ROLE_USER MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); request.setRequestURI(null); request.setServletPath("/test.do"); FilterInvocation filterInvocation = new FilterInvocation(request, new MockHttpServletResponse(), new MockFilterChain()); Collection<ConfigAttribute> attrs = definitionsource.getAttributes(filterInvocation); LOGGER.debug("### Pattern Matched url size is {} and Roles are {}", attrs.size(), attrs); assertTrue(attrs.contains(new SecurityConfig("ROLE_USER"))); // "/sale/index.do" ROLE_RESTRICTED request = new MockHttpServletRequest(); request.setMethod("POST"); request.setRequestURI(null); request.setServletPath("/sale/index.do"); filterInvocation = new FilterInvocation(request, new MockHttpServletResponse(), new MockFilterChain()); attrs = definitionsource.getAttributes(filterInvocation); LOGGER.debug("### Pattern Matched url size is {} and Roles are {}", attrs.size(), attrs); assertTrue(attrs.contains(new SecurityConfig("ROLE_RESTRICTED"))); } /** * 웹 접근이 허용되지 않은 URL로 접근 시 실패 테스트 * @throws Exception */ @Test public void testFailedURLAndRoleMapping() throws Exception { FilterSecurityInterceptor interceptor = (FilterSecurityInterceptor) context.getBean("filterSecurityInterceptor"); FilterInvocationSecurityMetadataSource definitionsource = interceptor.getSecurityMetadataSource(); // "/test.do" ROLE_USER MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); request.setRequestURI(null); request.setServletPath("/index.do"); FilterInvocation filterInvocation = new FilterInvocation(request, new MockHttpServletResponse(), new MockFilterChain()); Collection<ConfigAttribute> attrs = definitionsource.getAttributes(filterInvocation); LOGGER.debug("### Pattern Matched url is none"); assertNull(attrs); } /** * 웹 접근이 허용된 URL로 접근 시 Context 에서 지정한 로그인 화면으로 이동됨 검사 * @throws Exception */ @Test public void testSuccessfulUrlInvocation() throws Exception { final String loginPage = "/cvpl/EgovCvplLogin.do"; FilterChainProxy filterChainProxy = (FilterChainProxy) context.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN); //FilterChainProxy filterChainProxy = (FilterChainProxy) context.getBean(BeanIds.FILTER_CHAIN_PROXY); //////////////// MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("GET"); request.setServletPath("/test.do"); MockHttpServletResponse response = new MockHttpServletResponse(); MockFilterChain chain = new MockFilterChain(); filterChainProxy.doFilter(request, response, chain); assertTrue(response.getRedirectedUrl().indexOf(loginPage) >= 0); LOGGER.debug("### getRedirectedUrl {}", response.getRedirectedUrl()); LOGGER.debug("### getForwardedUrl {}", response.getForwardedUrl()); LOGGER.debug("### getIncludedUrl {}", response.getIncludedUrl()); LOGGER.debug("### getErrorMessage {}", response.getErrorMessage()); LOGGER.debug("### getContentAsString {}", response.getContentAsString()); ///////////// request = new MockHttpServletRequest(); request.setMethod("GET"); request.setServletPath("/sale/index.do"); response = new MockHttpServletResponse(); filterChainProxy.doFilter(request, response, chain); assertTrue(response.getRedirectedUrl().indexOf(loginPage) >= 0); LOGGER.debug("### getRedirectedUrl {}", response.getRedirectedUrl()); LOGGER.debug("### getForwardedUrl {}", response.getForwardedUrl()); LOGGER.debug("### getIncludedUrl {}", response.getIncludedUrl()); LOGGER.debug("### getErrorMessage {}", response.getErrorMessage()); LOGGER.debug("### getContentAsString {}", response.getContentAsString()); } /** * 웹 접근이 허용되지 않은 URL로 접근 시 Context 에서 지정한 로그인 화면으로 이동되지 않음 검사 * @throws Exception */ @Test public void testFailureUrlInvocation() throws Exception { //final String loginPage = "/cvpl/EgovCvplLogin.do"; FilterChainProxy filterChainProxy = (FilterChainProxy) context.getBean(BeanIds.FILTER_CHAIN_PROXY); //////////////// MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("GET"); request.setServletPath("/index.do"); MockHttpServletResponse response = new MockHttpServletResponse(); MockFilterChain chain = new MockFilterChain(); filterChainProxy.doFilter(request, response, chain); assertNull(response.getRedirectedUrl()); LOGGER.debug("### getRedirectedUrl is null"); //////////////// request = new MockHttpServletRequest(); request.setMethod("GET"); request.setServletPath("/sale/index.doit"); response = new MockHttpServletResponse(); chain = new MockFilterChain(); filterChainProxy.doFilter(request, response, chain); assertNull(response.getRedirectedUrl()); LOGGER.debug("### getRedirectedUrl is null"); } /** * 세션처리를 위한 UserDetails 확장 테스트 * @throws Exception */ @Test public void testUserDetailsExt() throws Exception { // 인증되지 않은 사용자 체크 Boolean isAuthenticated = EgovUserDetailsHelper.isAuthenticated(); assertFalse(isAuthenticated.booleanValue()); LOGGER.debug("### testUserDetailsExt 인증 : {}", isAuthenticated.booleanValue()); EgovUserDetailsVO user = (EgovUserDetailsVO)EgovUserDetailsHelper.getAuthenticatedUser(); assertNull(user); LOGGER.debug("### testUserDetailsExt 사용자정보 : {}", user); // 로그인 jimi UsernamePasswordAuthenticationToken login = new UsernamePasswordAuthenticationToken("jimi", "jimi"); AuthenticationManager authManager = (AuthenticationManager) context.getBean(BeanIds.AUTHENTICATION_MANAGER); SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(login)); // 인증된 사용자 검증 isAuthenticated = EgovUserDetailsHelper.isAuthenticated(); assertTrue(isAuthenticated.booleanValue()); LOGGER.debug("### testUserDetailsExt 인증 : {}", isAuthenticated.booleanValue()); // 검증 // ID : jimi user = (EgovUserDetailsVO)EgovUserDetailsHelper.getAuthenticatedUser(); assertNotNull(user); assertEquals("jimi", user.getUserId()); assertEquals("jimi test", user.getUserName()); assertEquals("19800604", user.getBirthDay()); assertEquals("1234567890123", user.getSsn()); LOGGER.debug("### testUserDetailsExt 사용자 : {}", user.getUserId()); // 로그인 login = new UsernamePasswordAuthenticationToken("test", "test"); authManager = (AuthenticationManager) context.getBean(BeanIds.AUTHENTICATION_MANAGER); SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(login)); // 인증된 사용자 검증 isAuthenticated = EgovUserDetailsHelper.isAuthenticated(); assertTrue(isAuthenticated.booleanValue()); // ID : test user = (EgovUserDetailsVO)EgovUserDetailsHelper.getAuthenticatedUser(); assertNotNull(user); assertEquals("test", user.getUserId()); assertEquals("Kim, Young-Su", user.getUserName()); assertEquals("19800604", user.getBirthDay()); assertEquals("1234567890123", user.getSsn()); LOGGER.debug("### testUserDetailsExt 사용자 : {}", user.getUserId()); // 로그인 login = new UsernamePasswordAuthenticationToken("user", "user"); authManager = (AuthenticationManager) context.getBean(BeanIds.AUTHENTICATION_MANAGER); SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(login)); // 인증된 사용자 검증 isAuthenticated = EgovUserDetailsHelper.isAuthenticated(); assertTrue(isAuthenticated.booleanValue()); // ID : test user = (EgovUserDetailsVO)EgovUserDetailsHelper.getAuthenticatedUser(); assertNotNull(user); assertEquals("user", user.getUserId()); assertEquals("Hong Gil-dong", user.getUserName()); assertEquals("19800603", user.getBirthDay()); assertEquals("8006041227717", user.getSsn()); LOGGER.debug("### testUserDetailsExt 사용자 : {}", user.getUserId()); // 로그인 login = new UsernamePasswordAuthenticationToken("buyer", "buyer"); authManager = (AuthenticationManager) context.getBean(BeanIds.AUTHENTICATION_MANAGER); SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(login)); // 인증된 사용자 검증 isAuthenticated = EgovUserDetailsHelper.isAuthenticated(); assertTrue(isAuthenticated.booleanValue()); // ID : buyer user = (EgovUserDetailsVO)EgovUserDetailsHelper.getAuthenticatedUser(); assertEquals("buyer", user.getUserId()); assertEquals("Lee, Man-hong", user.getUserName()); assertEquals("19701231", user.getBirthDay()); assertEquals("1234567890123", user.getSsn()); LOGGER.debug("### testUserDetailsExt 사용자 : {}", user.getUserId()); } /** * 지정된 Role 조회 테스트 * @throws Exception */ @Test public void testAuthoritiesAndRoleHierarchy() throws Exception { // user User : ROLE_USER UsernamePasswordAuthenticationToken login = new UsernamePasswordAuthenticationToken("user", "user"); AuthenticationManager authManager = (AuthenticationManager) context.getBean(BeanIds.AUTHENTICATION_MANAGER); SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(login)); LOGGER.debug("DEBUG : {}", EgovUserDetailsHelper.getAuthorities()); List<String> authorities = EgovUserDetailsHelper.getAuthorities(); // 1. authorites 에 ROLE_USER 권한이 있는지 체크 TRUE/FALSE LOGGER.debug("########### user ROLES are {}", authorities); assertTrue(authorities.contains("ROLE_USER")); assertTrue(authorities.contains("ROLE_RESTRICTED")); assertTrue(authorities.contains("IS_AUTHENTICATED_ANONYMOUSLY")); assertTrue(authorities.contains("IS_AUTHENTICATED_FULLY")); assertTrue(authorities.contains("IS_AUTHENTICATED_REMEMBERED")); // 2. authorites 에 ROLE 이 여러개 설정된 경우 for (Iterator<String> it = authorities.iterator(); it.hasNext();) { String auth = it.next(); LOGGER.debug("########### user ROLE is {}", auth); } // 3. authorites 에 ROLE 이 하나만 설정된 경우 String auth = (String) authorities.toArray()[0]; LOGGER.debug("########### user ROLE is {}", auth); // buyer USER : ROLE_RESTRICTED login = new UsernamePasswordAuthenticationToken("buyer", "buyer"); authManager = (AuthenticationManager) context.getBean(BeanIds.AUTHENTICATION_MANAGER); SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(login)); LOGGER.debug("DEBUG : {}", EgovUserDetailsHelper.getAuthorities()); authorities = EgovUserDetailsHelper.getAuthorities(); LOGGER.debug("########### buyer ROLES are {}", authorities); assertFalse(authorities.contains("ROLE_USER")); assertFalse(authorities.contains("ROLE_ADMIN")); assertTrue(authorities.contains("ROLE_RESTRICTED")); assertTrue(authorities.contains("IS_AUTHENTICATED_ANONYMOUSLY")); assertTrue(authorities.contains("IS_AUTHENTICATED_FULLY")); assertTrue(authorities.contains("IS_AUTHENTICATED_REMEMBERED")); // test USER : ROLE_ADMIN login = new UsernamePasswordAuthenticationToken("test", "test"); authManager = (AuthenticationManager) context.getBean(BeanIds.AUTHENTICATION_MANAGER); SecurityContextHolder.getContext().setAuthentication(authManager.authenticate(login)); LOGGER.debug("DEBUG : {}", EgovUserDetailsHelper.getAuthorities()); authorities = EgovUserDetailsHelper.getAuthorities(); LOGGER.debug("########### test ROLES are {}", authorities); assertTrue(authorities.contains("ROLE_USER")); assertTrue(authorities.contains("ROLE_ADMIN")); assertTrue(authorities.contains("ROLE_RESTRICTED")); assertTrue(authorities.contains("IS_AUTHENTICATED_ANONYMOUSLY")); assertTrue(authorities.contains("IS_AUTHENTICATED_FULLY")); assertTrue(authorities.contains("IS_AUTHENTICATED_REMEMBERED")); } }