How to add a header to a MockMvc request, depending on test annotation? - spring-test

As a follow up to this question, I wonder how to transparently add an "Authorization" header to a MockHttpServletRequestBuilder, only if a given annotation is present on the test.
sample:
#RunWith(SpringRunner.class)
#EnableSpringDataWebSupport
#WebMvcTest(MyController.class)
#Import({WebSecurityConfig.class})
public class MyControllerTest {
#Autowired
protected MockMvc mockMvc;
#Test
#WithJwt(principal = "admin", authorities = {"READ_USERS"})
public void readUsersAuthorityCanListUsers() throws Exception {
final List<User> users = Arrays.asList(admin, user);
when(userRepo.findAll(any(Pageable.class))).thenReturn(new PageImpl<>(users));
this.mockMvc
.perform(get("/")
.header("Authorization", "Bearer foo"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(users.size())));
}
}
How to post-process the request builder to automatically apply .header("Authorization", "Bearer foo") if the test is decorated with #WithJwt?

I ended to wrap MockMvc and proxy method calls, adding the Authorization header.
This would be over-kill for a single header, but this MockMvcHelper also sets content-type and accept headers, provides shortcuts to issue simple api calls (get, post, put patch, delete with default headers and serialization), etc.
You can have a look at this wrapper at the end of the solution to other question: https://stackoverflow.com/a/48540159/619830

Related

Web API 2 Basic Authentication and allow actions not marked [Authorize]

I have been looking at Basic Authentication in Web Api2 and don’t seem to find an explanation for something I am confused about.
I created a web api application project with individual authentication in Visual studio 2017.
I have the default code
public class ValuesController : ApiController
{
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
public string Get(int id)
{
return "value";
}
}
I call these actions via postman, browser etc all good.
If I add the [Authorize] attribute to one of the methods I get 401 unauthorized response as expected.
So far so good.
I then add basic authentication by creating a class derived from AuthorizationFilterAttribute
public class BasicAuthenticationAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
var authHeader = actionContext.Request.Headers.Authorization;
if (authHeader != null)
{
var authenticationToken = actionContext.Request.Headers.Authorization.Parameter;
var decodedAuthenticationToken = Encoding.UTF8.GetString(Convert.FromBase64String(authenticationToken));
var usernamePasswordArray = decodedAuthenticationToken.Split(':');
var userName = usernamePasswordArray[0];
var password = usernamePasswordArray[1];
var isValid = userName == "ade" && password == "password";
if (isValid)
{
var principal = new GenericPrincipal(new GenericIdentity(userName), null);
HttpContext.Current.User = principal;
return;
}
}
}
HandleUnathorized(actionContext);
}
private static void HandleUnathorized(HttpActionContext actionContext)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
actionContext.Response.Headers.Add("WWW-Authenticate", "Basic Scheme='Data' location = 'http://localhost:");
}
I register the filter in WebApiConfig.cs
config.Filters.Add(new BasicAuthenticationAttribute());
I use postman to call the action marked with [Authorize] and send with header Authorization: Basic YWRlOnBhc3N3b3Jk
The request is authorized and I get my action response. All good.
Now I call the action that is not marked with [Authorize] without a Authorization header from postman expecting to get a response but the OnAuthorization is called and obviously returns HandleUnathorized(actionContext); I only expected the OnAuthorization method to be called where an action is marked with [Authorize]
So now I am thinking what is the point of the [Authorize] attribute because OnAuthorization is called regardless so what is the point of marking actions [Authorize] attribute?
Secondly, I added the following method to my class
private static bool SkipAuthorization(HttpActionContext actionContext)
{
Contract.Assert(actionContext != null);
return actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any()
|| actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any();
}
I call this method at the beginning of OnAuthorization
if (SkipAuthorization(actionContext)) return;
If I mark my actions with [AllowAnonymous] it works.
If there is no [Authorize] attribute on the controller or specific actions then surely the OnAuthorization should also be skipped?
I just don't see the point of using [Authorize], I am clearly missing something here, am I doing something wrong or do I need to mark the actions with [AllowAnonymous] to exclude them.
If you are using [Authorize] attribute and windows authentication, then authorization will done automatically, you don't need to do any special configuration, but any special case if you need to override [Authorize] class then your class is like below,
Instead of inheriting AuthorizationFilterAttribute, you can
inherit AuthorizeAttribute
public class BasicAuthenticationAttribute : AuthorizeAttribute
{
//your override methods
}
Instead of using [Authorize] attribute, use your derived class name. In your case use [BasicAuthenticationAttribute], not [Authorize]
Thanks Fran you set me off on the right path.
I commented out the following line
config.Filters.Add(new BasicAuthenticationAttribute());
I used the following attributes in controller
public class ValuesController : ApiController
{
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
[Authorize]
[BasicAuthentication]
public string Get(int id)
{
return "value";
}
}
If I called the action get() I got a response, OnAuthorisation was not called.
If I call get(int id) I get 401 Unauthorised and OnAuthorisation is not called.
I removed the [Authorize] from get(int id) action
[BasicAuthentication]
public string Get(int id)
{
return "value";
}
and it all worked, OnAuthorisation was called as expected.

How to validate a REST Assured request across multiple java methods?

I am writing REST Assured tests using Cucumber.
Here is my feature file:
Given I want to GET a client
When I request a client
Then the status code is "theStatusCode"
And the id returned is "expectedClientId"
The below method is called within the Step Definition of the "And" in my feature file
public void validateResponseBody(String expectedClientId){
RestAssured.given()
.when()
.get(completeURL)
.then()
.statusCode(Integer.parseInt(theStatusCode))
.and()
.body("Client.Id", equalTo(expectedClientId));
}
This method currently works, but how do I split the validation?
I.e. how can I break this up to validate the Status Code in one method, & validate the Client Id in another method without having to send the request twice?
Save response to variable:
public void validate() {
ValidatableResponse response = RestAssured.given()
.when()
.get(completeURL)
.then();
validateStatusCode(response, statusCode);
validateResponseBody(response, expectedClientId);
}
public void validateStatusCode(ValidatableResponse response, String statusCode) {
response
.statusCode(Integer.parseInt(theStatusCode));
}
public void validateResponseBody(ValidatableResponse response, String expectedClientId) {
response
.body("Client.Id", equalTo(expectedClientId));
}
I suggest to make changes in Feature File.
New File should be given below. You don't need a when statement here.
Scenario: I want to GET a client
Given I request a client
Then the status code is "theStatusCode"
And the id returned is "expectedClientId"
#Given("I request a client$")
public void validate()
{
ValidatableResponse validatableResponse = RestAssured.given()
.when()
.get(completeURL)
.then();
}
#Then("the status code is \"([^\"]*)\"$")
public void validateStatusCode(String statusCode)
{
validatableResponse.assertThat().statusCode(Integer.parseInt(theStatusCode));
}
#And("the id returned is \"([^\"]*)\"$")
public void validateClientId(String expectedClientId)
{
validatableResponse.assertThat().body("Client.Id", equalTo(expectedClientId));
}

How to change request headers in .NETCore2 implementations

For testing purposes I need to alter the Authorization header of an incoming HttpRequest - from Basic to Bearer (the processing is done in a ServiceStack plugin which acts during the PreRequestFilters phase).
This used to work in ServiceStack 4.5.x version (.NET Framework 4.6.2) but fails once I've upgraded the code to 5.0.2 and NETCore2.
Looking into the source code I see the NETCore implementation uses NetCoreRequest class for request implementation and NetCoreHeadersCollection class to implement the headers collection.
Unfortunately there's no way to alter the headers in the above mentioned collection, this is what I see in source code:
public override void Add(string name, string value) => throw new NotSupportedException();
public override void Clear() => throw new NotSupportedException();
public override void Remove(string name) => throw new NotSupportedException();
public override void Set(string key, string value) => throw new NotSupportedException();
Is there another way of altering the request headers before they hit the request filters and the service in NETCore implementation?
Thanks,
Emil Petrisor
You can access .NET Core's Request/Response header collections with:
PreRequestFilters.Add((httpReq, httpRes) => {
if (httpReq.OriginalRequest is HttpRequest coreReq)
{
coreReq.Headers[HttpHeaders.Authorization] = ...;
}
if (httpRes.OriginalResponse is HttpResponse coreRes)
{
var authHeader = coreRes.Headers[HttpHeaders.Authorization];
}
});

How would I change a ServiceStack response DTO

I'm working on an API where I'd like to be able to customize the response structure based on a parameter from the client. Response filters seem like a good place to do this in order to avoid doing so in each service or action. The problem is that while I have access to the response DTO returned by the action, and could change its properties, I can't find how or where to replace the object entirely.
Naively replacing the object in the response filter did not work, but this help illustrate what I'm trying to do:
public class ChangeResponseAttribute : ResponseFilterAttribute
{
public override void Execute(IHttpRequest req, IHttpResponse res, object responseDto)
{
var overrideText = req.QueryString["override"];
if (!String.IsNullOrEmpty(overrideText))
responseDto = new { Message = overrideText };
}
}
[ChangeResponse]
public class TodosService : Service
{
public object Get(Todos request)
{
return new object[0];
}
}
It looks like another option would be to write the custom response directly & end the request, but that would bypass any other processing left to do by ServiceStack.
Is there a better place to do this than a response filter? Or do I need to bite the bullet and return the optimal DTO in each action?
You can't change the Response DTO in a filter, but yes one option is to write the response in the filter itself (see this answer for an example of how to do this).
The other option is to use a ServiceRunner and override the OnAfterExecute() custom hook which does let you modify the response returned, e.g:
public class MyServiceRunner<T> : ServiceRunner<T>
{
public override object OnAfterExecute(
IRequestContext requestContext, object response)
{
// Called just after any Action is executed
var overrideText = req.Get<IHttpRequest>().QueryString["override"];
return !string.IsNullOrEmpty(overrideText)
? new { Message = overrideText } : null;
}
}
To get ServiceStack to use it you need to override the CreateServiceRunner method in your AppHost, e.g:
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(
ActionContext actionContext)
{
return new MyServiceRunner<TRequest>(this, actionContext);
}

Control Cache from Front End

I would like to control the cache from the front end when certain calls are made in PracticeUpdate.
For example, when calling /api/GetAllTags from the javascript function GetAllTags, I can see in Fiddler that the return header for cache-control is set to no-cache. Is it possible to modify this in the api?
All you need to do is get Access to the HttpResponseMessage object of the request. You can do this inside a controller action by asking the Request property of the controller to create the response for you:
var response = Request.CreateResponse(HttpStatusCode.OK);
Then you can access the CacheControl object via the Headers like so:
response.Headers.CacheControl = new CacheControlHeaderValue
{
Public = true, MaxAge = TimeSpan.FromMinutes(5)
};
You could also make use of an ActionFilter in this scenario, so caching can be applied to an ApiController Action method via an attribute:
public class HttpCacheForMinutesAttribute : ActionFilterAttribute
{
private readonly int _duration;
public HttpCacheForMinutesAttribute(int duration)
{
_duration = duration;
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
actionExecutedContext.Response.Headers.CacheControl = new CacheControlHeaderValue
{
Public = true, MaxAge = TimeSpan.FromMinutes(_duration)
};
}
}
Default caching policy for Web API is no caching.
You can add the caching into each action or just use a framework to do that for you such as CacheCow which is full implementation of HTTP caching in both client (when you use HttpClient) and server.

Resources