I am testing an application written with Java and Spring Boot and I have a question.
My test simulates an HTTP request which is valid only if the customData data is placed inside the Cookie header.
This is the code for my simple test:
#Test
public void myFristTest() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post(MY_URL)
.header("Cookie", "customData=customString")
.accept(MediaType.APPLICATION_JSON_VALUE)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(ConversionUtil.objectToString(BODY_OF_MY_REQUEST)))
.andExpect(status().isCreated());
}
Unfortunately this test fails. The Java code that goes to test is the following:
String customData;
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("customData")) {
customData = cookie.getValue();
}
}
}
if(customData != null) {
// code that returns HTTP status isCreated
} else {
throw new HttpServerErrorException(HttpStatus.FOUND, "Error 302");
}
In practice, it seems that the customData string, which should be taken from the request header Cookie, is not found! So the test only evaluates the else branch and actually also in the stacktrace tells me that the test was expecting the status isCreated but the status 302 is given.
How can this be explained, since the application (without test) works? I imagine that .header("Cookie", "customData=customString") in my test doesn't do what I want, that is, it doesn't set the header cookie correctly, which is why my method fails. How do I do a proper test that really inserts Cookie header into the request?
I use Junit 4.
The MockHttpServletRequestBuilder class provides the cookie builder method to add cookies. The MockHttpServletRequest created internally for the test ignores "Cookie" headers added through the header method.
So create a Cookie and add it
Cookie cookie = new Cookie("customData", "customString");
mockMvc.perform(MockMvcRequestBuilders.post(MY_URL)
.cookie(cookie)
.accept(MediaType.APPLICATION_JSON_VALUE)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(ConversionUtil.objectToString(BODY_OF_MY_REQUEST)))
.andExpect(status().isCreated());
Related
I have the following spring cloud contract:
package contracts.teams
import org.springframework.cloud.contract.spec.Contract
Contract.make {
name"d Find team roles by filters"
description "Find team roles by filters"
request {
method "POST"
url "api/team/findTeamRolesByFilters"
headers {
contentType applicationJson()
accept applicationJson()
header"Authorization", execute('bearerOfAccessToken()')
}
body execute('getRequestForFindTeamRolesByFilters()')
}
response {
status OK()
headers {
contentType applicationJson()
}
body execute('getResponseForFindTeamRolesByFilters()')
}
}
I call the getResponseForFindTeamRolesByFilters() at the response in order to generate a dynamic response from the server. The reason could for example be an auto generated id that is coming from the DB.
The generated string from the getResponseForFindTeamRolesByFilters() is a valid JSON that unfortunately is ignored and returns always true when the test run.
I have noticed this when I replace the execute method with a static response like the following one:
"""
{
"success": "false"
}
"""
In this case the response is being validated correctly and fails the test in case it does not match.
What I said is being confirmed by the test generated code as it can be seen here:
// then:
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.header("Content-Type")).matches("application/json.*");
// and:
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
getResponseForFindTeamRolesByFilters();
As you can see there is no assertion. It simply calls the method that generates the json.
How am I supposed to make the test check the dynamic json response?
Thank you!
I call the getResponseForFindTeamRolesByFilters() at the response in order to generate a dynamic response from the server.
You should not do it. Contract tests should not access the database.
For the consumer you should do
request {
method "POST"
url "api/team/findTeamRolesByFilters"
headers {
contentType applicationJson()
accept applicationJson()
header"Authorization", execute('bearerOfAccessToken()')
}
body $(producer(execute('getRequestForFindTeamRolesByFilters()')), consumer("some value to be put on the consumer side"))
}
For the producer
response {
status OK()
headers {
contentType applicationJson()
}
body $(producer(execute('getResponseForFindTeamRolesByFilters()')), consumer("something in the stub"))
}
Am trying the reuse the restassured get call in the testng api tests, but the restassured instance in using the cookies received from the previous response.
Tried RestAssured.reset(); but this doesn't help in flushing the cookies we got from earlier request/response.
Reason for this question - As the get endpoint behaves differently if there is a session cookie exist on the request.
#Test // TestNG test
public void test_1(){
//Set Cookie
Cookie cookie = new Cookie.Builder("COOKIENAME","COOKIEVALUE").setDomain("*.com").setPath("/").setExpiryDate(SOMELATERDATE).build();
RestAssured.baseURI = https://ENV_URL;
Response response = RestAssured.given().log().all()
.cookies(new Cookies(cookie)).when().get("/END_POINT").then().extract().response().prettyPeek();
RestAssured.reset();
}
#Test // TestNG test
public void test_2(){
//Set Cookie
Cookie cookie = new Cookie.Builder("COOKIENAME", "COOKIEVALUE").setDomain("*.com").setPath("/").setExpiryDate(SOMELATERDATE).build();
RestAssured.baseURI = https://ENV_URL;
// Still Reuses the cookie received from previous response
Response response = RestAssured.given().log().all()
.cookies(new Cookies(cookie)).when().get("/END_POINT").then().extract().response().prettyPeek();
RestAssured.reset();
}
Use CookieFilter. If you want to exclude the a particular cookie you can use the following code.
#Test
public void sampletest(){
CookieFilter cookieFilter = new CookieFilter();
Response response = RestAssured.given().
cookie("foo", "bar").
filter(cookieFilter). // Reuse the same cookie filter
// if "foo" is stored in cookieFilter it won't be applied because it's already applied explicitly
expect().
statusCode(200).
when().
get("/y");
}
I am trying to implent CSRF-protection with Spring Security (4.1.3) and Angular 2.0.1
There are many sources to related topics but I cannot find a clear instruction. Some of the statements even contradict each other.
I read about springs way of doing it (though the guide describes the Angular 1 way) Spring Security Guide with Angular
IT implies, that with
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
everything should work "out of the box".
Even further the angular guide to security describes the CSRF-protection as build in.
In my enviroment the POST looks like this:
There is an OPTIONS-call which returns POST, 200 OK and a XSRF-TOKEN - cookie.
my http.post adds an authorization-header and adds the RequestOption "withCredentials"
It sends three cookies, two JSessionID's and an XSRF-TOKEN that is different from the one recieved by the OPTIONS-call, no XSRF-header.
Debugging into the Spring CsrfFilter shows me that it looks for a header named X-XSRF-TOKEN and compares it to the token in the cookie named XSRF-TOKEN.
Why doesn't Angular send the header, too?
How is this secure if Spring only checks the provided cookie and the provided header with no serverside action whatsoever?
There are some similar questions like this but the only answer with 0 upvotes seems (to me) plain wrong, as CSRF, from my understanding, has to have a serverside check for the cookie validation.
This question only provides information on how to change the cookie or header name as explained here
What am I missing here? I doubt there is a mistake in the Spring Security implementation but I cannot quite get it to work.
Any ideas?
POST-call
login(account: Account): Promise<Account> {
let headers = new Headers({ 'Content-Type': 'application/json' });
headers.append('X-TENANT-ID', '1');
headers.append('Authorization', 'Basic ' + btoa(account.userName + ':' + account.password));
let options = new RequestOptions({ headers: headers, withCredentials:true });
return this.http.post(this.loginUrl, account, options).toPromise()
.then(this.extractData)
.catch(this.handleError)
}
Spring Security Config
[] csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
The problem was the application path. Spring has the option to set the cookie-path in its pipeline but it is not released yet.
I had to write my own implementation for the CsrfTokenRepository which would accept a different cookie path.
Those are the relevant bits:
public final class CookieCsrfTokenRepository implements CsrfTokenRepository
private String cookiePath;
#Override
public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) {
String tokenValue = token == null ? "" : token.getToken();
Cookie cookie = new Cookie(this.cookieName, tokenValue);
cookie.setSecure(request.isSecure());
// cookie.setPath(getCookiePath(request));
if (this.cookiePath != null && !this.cookiePath.isEmpty()) {
cookie.setPath(this.cookiePath);
} else {
cookie.setPath(getRequestContext(request));
}
if (token == null) {
cookie.setMaxAge(0);
} else {
cookie.setMaxAge(-1);
}
if (cookieHttpOnly && setHttpOnlyMethod != null) {
ReflectionUtils.invokeMethod(setHttpOnlyMethod, cookie, Boolean.TRUE);
}
response.addCookie(cookie);
}
public void setCookiePath(String path) {
this.cookiePath = path;
}
public String getCookiePath() {
return this.cookiePath;
}
I have a jax-rs endpoint. The purpose of the endpoint is to authorize a user. I need to login details inside a cookie. Below I have mentioned the related part of my code.
public Response authorize(#Context HttpServletRequest request) throws URISyntaxException {
if (authnResult.isAuthenticated()) {
//TODO create a cookie to maintain login state
Cookie authCookie = new Cookie(FrameworkConstants.COMMONAUTH_COOKIE, "test");
authCookie.setSecure(true);
authCookie.setHttpOnly(false);
authCookie.setMaxAge(5 * 60);
}
EDIT:
This is my first time when creating cookies. I followed some tutorials. In these tutorials it has added the created cookie to the response. But inside the endpoint I can't access the response. So how can I create the cookie? Please advice me.
Updated code:
public Response authorize(#Context HttpServletRequest request) throws URISyntaxException {
NewCookie cookie = new NewCookie("CookieName","CookieValue");
Response.ResponseBuilder builder = Response.ok("Cool Stuff");
builder.cookie(cookie);
Response response=builder.build();
Cookie[] cookies = request.getCookies();
}
what I need to know is how to access the newly created cookie.
You can create a javax.ws.rs.core.NewCookie. There are a bunch of different constructors, just go through the API docs.
Then you can add cookies through ResponseBuilder#cookie(NewCookie). So for example:
#GET
public Response getCookie() {
NewCookie cookie = new NewCookie("Name", "Value", "path", "domain",
"comment", 300, true, true);
ResponseBuilder builder = Response.ok("Cool Stuff");
builder.cookie(cookie);
return builder.build();
}
UPDATE (with complete example)
#Path("cookie")
public class CookieResource {
#GET
public Response getCookie(#CookieParam("A-Cookie") String cookie) {
Response response = null;
if (cookie == null) {
response = Response.ok("A-Cookie: Cookie #1")
.cookie(new NewCookie("A-Cookie", "Cookie #1"))
.build();
return response;
} else {
String cookieNum = cookie.substring(cookie.indexOf("#") + 1);
int number = Integer.parseInt(cookieNum);
number++;
String updatedCookie = "Cookie #" + number;
response = Response.ok("A-Cookie: " + updatedCookie)
.cookie(new NewCookie("A-Cookie", updatedCookie))
.build();
return response;
}
}
}
After 38 requests, you can see the result. I used a Firefox plugin Firebug. You can see the sent cookie #37, and returned cookie #38
If you need help trying to access the cookie from the client (as suggested in your comment), that may be suitable for another question on SO. Maybe off topic for this discussion, as it will rely on another technology. If this is not what you are looking for, then maybe a better explanation of exactly what you are trying to accomplish would help.
I have a scenario where I want to store session information across multiple sessions in Application # 2. We have two applications deployed on a tomcat server. Our use case is as follows:
A. Web Application # 1 makes a HTTP Post request to Application # 2 using a HTTP Rest Client. POST request contains a JSON http request body encapsulating the data to be send to Application # 2. The code block is as follows:
RestTemplate template = new RestTemplate();
final SearchCustomer customer = new SearchCustomer();
restTemplate.execute(
SEND_CUSTOMER_PROFILE, HttpMethod.POST,
new SearchRequestCallback(searchCustomer), null);
The request callback function is
static class SearchRequestCallback implements RequestCallback {
/**
* Write a JSON response to the request body.
*/
#Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
HttpHeaders httpHeaders = request.getHeaders();
List<MediaType> acceptableMediaTypes = new LinkedList<>();
acceptableMediaTypes.add(MediaType.APPLICATION_JSON);
httpHeaders.setAccept(acceptableMediaTypes);
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
request.getBody().write(
new Gson().toJson(this.searchCustomer).getBytes(StandardCharsets.UTF_8.displayName()));
}
}
The second application has a Spring controller with the following set up
#Controller
public class SearchCustomerController {
/**
* Builds customer profile knowledge graph.
*
* <p>This is invoked as an synchronous request.
*/
#RequestMapping(value="/searchProfilePayload.go", method=RequestMethod.POST)
#ResponseStatus(HttpStatus.OK)
public void constructSearchCustomerProfileKnowledgeGraph(
#RequestBody final SearchCustomer customer, HttpServletRequest request) {
UserContext userContext =
(UserContext) request.getSession().getAttribute("userContext");
if (userContext == null) {
// Perform heavy operation to fetch user session.
userContext = UserContextHelper.getUserContext(request);
request.getSession("userContext", userContext)
}
userContext.setCustomerProfile(customer);
}
}
When I make a call to another URI within the application # 2 say via browser, I want it done in such as way that the session attributes are retained when making this call. Is there a way to do that?
I know about URL rewriting that stores JSESSIONIDin the cookie, but I don't think how you can set the value when making a rest call, and using the same JESSIONID to maintain session attributes.
Is there a better way to do this? These have no answers. I have looked at these links, but none seem to answer my question.
HTTP and Sessions
comparison of ways to maintain state
jraahhali is spot on.
Set the cookie header with the value of JSESSIONID=${sessionId} or use it directly in the url as per the URL rewriting link.
First step is to retrieve the JSESSIONID from the initial response (this will depend on how you decide to set the session id - URL or Cookies, lets assume by cookie for now)
#Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
HttpHeaders httpHeaders = request.getHeaders();
List<MediaType> acceptableMediaTypes = new LinkedList<>();
acceptableMediaTypes.add(MediaType.APPLICATION_JSON);
httpHeaders.setAccept(acceptableMediaTypes);
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
request.getBody().write(
new Gson().toJson(this.searchCustomer).getBytes(StandardCharsets.UTF_8.displayName()));
ClientHttpResponse response = request.execute();
String sessionId = response.getHeaders().get(HttpHeaders.SET_COOKIE).split(":")[1].trim(); // I didnt test this, will prolly get a NPE :P
this.sessionId = sessionId;
}
Then in subsequent requests (ie from the app #1 or a browser or whatever)
if (this.sessionId != null && !this.sessionId.equals(""))
httpHeaders.set(HttpHeaders.COOKIE, "JSESSIONID=" + this.sessionId);
// ...
request.execute();
Note if you really want to use a browser as the other client then I would use the URL rewriting method for ease of use ...