package io.oasp.gastronomy.restaurant.general.common.api.security; import java.util.List; import org.json.JSONObject; import org.junit.Test; import org.skyscreamer.jsonassert.JSONAssert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.TestPropertySource; import org.springframework.web.client.RestTemplate; import io.oasp.gastronomy.restaurant.SpringBootApp; import io.oasp.gastronomy.restaurant.general.common.api.to.UserDetailsClientTo; import io.oasp.gastronomy.restaurant.general.common.base.AbstractRestServiceTest; import io.oasp.gastronomy.restaurant.general.service.impl.rest.SecurityRestServiceImpl; /** * This class tests the login functionality of {@link SecurityRestServiceImpl}. */ @SpringApplicationConfiguration(classes = SpringBootApp.class) @TestPropertySource(properties = { "flyway.locations=filesystem:src/test/resources/db/tablemanagement" }) public class SecurityRestServiceImplTest extends AbstractRestServiceTest { /** Logger instance. */ private static final Logger LOG = LoggerFactory.getLogger(SecurityRestServiceImplTest.class); private static final String _CSRF = "_csrf"; private static final String X_CSRF_TOKEN = "X-CSRF-TOKEN"; private RestTemplate template = new RestTemplate(); /** * Test the login functionality as it will be used from a JavaScript client. */ @Test @DirtiesContext public void testLogin() { String userName = "waiter"; String tmpPassword = "waiter"; ResponseEntity<String> postResponse = login(userName, tmpPassword); LOG.debug("Body: " + postResponse.getBody()); assertThat(postResponse.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(postResponse.getHeaders().containsKey(HttpHeaders.SET_COOKIE)).isTrue(); } /** * This test depends on the login and csrf functionaility. These are tested in {@link #testLogin()} and * {@link #testGetCsrfToken()} respectively. */ @Test @DirtiesContext public void testGetCurrentUser() { String userName = "waiter"; String tmpPassword = "waiter"; String tmpUrl = "http://localhost:" + String.valueOf(this.port) + "/services/rest/security/v1/currentuser"; ResponseEntity<String> loginResponse = login(userName, tmpPassword); String jsessionId = loginResponse.getHeaders().get(HttpHeaders.SET_COOKIE).get(0); ResponseEntity<String> csrfEntity = csrf(jsessionId); HttpEntity<String> entity = new HttpEntity<>(prepareHeaders(new StrTup(HttpHeaders.COOKIE, jsessionId), new StrTup(X_CSRF_TOKEN, new JSONObject(csrfEntity.getBody()).getString("token")))); ResponseEntity<UserDetailsClientTo> userEntity = this.template.exchange(tmpUrl, HttpMethod.GET, entity, UserDetailsClientTo.class); UserDetailsClientTo userDetailsClientTo = userEntity.getBody(); assertThat(userDetailsClientTo.getId()).isNotNull(); assertThat(userDetailsClientTo.getFirstName()).isNotEmpty(); assertThat(userDetailsClientTo.getLastName()).isNotEmpty(); assertThat(userDetailsClientTo.getName()).isNotEmpty(); assertThat(userDetailsClientTo.getRole()).isNotNull(); LOG.debug("GET Response User: " + userDetailsClientTo.getFirstName()); } /** * This test depends on the successful login which is tested by {@link #testLogin()}. Then, a csrf token is queried * and checked. */ @Test @DirtiesContext public void testGetCsrfToken() { String userName = "waiter"; String tmpPassword = "waiter"; ResponseEntity<String> loginResponse = login(userName, tmpPassword); LOG.debug("Body: " + loginResponse.getBody()); assertThat(loginResponse.getStatusCode()).isEqualTo(HttpStatus.OK); List<String> setCookie = loginResponse.getHeaders().get(HttpHeaders.SET_COOKIE); assertThat(setCookie).hasSize(1); String jsessionId = setCookie.get(0); ResponseEntity<String> csrfEntity = csrf(jsessionId); JSONObject actual = new JSONObject(csrfEntity.getBody()); JSONObject expected = new JSONObject("{\"headerName\":\"" + X_CSRF_TOKEN + "\",\"parameterName\":\"" + _CSRF + "\"}"); JSONAssert.assertEquals(expected, actual, false); assertThat(actual.get("token")).isNotNull(); LOG.debug("GET Response Csrf: " + actual.toString()); } /** * Performs a query for a csrf token. * * @param jsessionId the cookie returned after a successful login. * @return a {@link ResponseEntity} containing an JSON object as its body. */ private ResponseEntity<String> csrf(String jsessionId) { String tmpUrl = "http://localhost:" + String.valueOf(this.port) + "/services/rest/security/v1/csrftoken"; HttpHeaders headers = prepareHeaders(new StrTup(HttpHeaders.COOKIE, jsessionId)); HttpEntity<String> entity = new HttpEntity<>(headers); ResponseEntity<String> responseEntity = this.template.exchange(tmpUrl, HttpMethod.GET, entity, String.class); return responseEntity; } /** * Performs the login as required by a JavaScript client. * * @param userName the username of the user * @param tmpPassword the password of the user * @return @ {@link ResponseEntity} containing containing a cookie in its header. */ private ResponseEntity<String> login(String userName, String tmpPassword) { String tmpUrl = "http://localhost:" + String.valueOf(this.port) + "/services/rest/login"; HttpEntity<String> postRequest = new HttpEntity<>( prepareJson(new StrTup("j_username", userName), new StrTup("j_password", tmpPassword)), prepareHeaders()); ResponseEntity<String> postResponse = this.template.exchange(tmpUrl, HttpMethod.POST, postRequest, String.class); return postResponse; } /* * Helpers (class and methods) */ private class StrTup { private String key; private String value; public StrTup(String key, String value) { this.key = key; this.value = value; } /** * @return key */ public String getKey() { return this.key; } /** * @return value */ public String getValue() { return this.value; } } private String prepareJson(StrTup... stringTuples) { JSONObject result = new JSONObject(); for (StrTup t : stringTuples) { result.put(t.getKey(), t.getValue()); } return result.toString(); } private HttpHeaders prepareHeaders(StrTup... stringTuples) { HttpHeaders result = new HttpHeaders(); if (stringTuples != null) { for (StrTup t : stringTuples) { result.add(t.getKey(), t.getValue()); } } result.setContentType(MediaType.APPLICATION_JSON); return result; } }