Unit testing IAuthenticationFilter in WebApi 2 - c#

I'm trying to unit test a basic authentication filter I've written for a WebApi 2 project, but i'm having trouble mocking the HttpAuthenticationContext object required in the OnAuthentication call.
public override void OnAuthentication(HttpAuthenticationContext context)
{
base.OnAuthentication(context);
var authHeader = context.Request.Headers.Authorization;
... the rest of my code here
}
The line in the implementation that I'm trying to set up for mocking is the one that sets the authHeader variable.
However, I can't mock the Headers object because its sealed. And I can't mock the request and set a mocked headers because its a non-virtual property. And so on up the chain all the way to the context.
Has anyone successfully unit tested a new IAuthenticationFilter implementation?
I'm using Moq but I'm sure I could follow along in any mocking library if you have sample code.
Thanks for any help.

It is possible to achieve what you wanted however as none of the objects in the chain context.Request.Headers.Authorization exposes virtual properties Mock or any other framework won't provide much help for you. Here is the code for obtaining HttpAuthenticationContext with mocked values:
HttpRequestMessage request = new HttpRequestMessage();
HttpControllerContext controllerContext = new HttpControllerContext();
controllerContext.Request = request;
HttpActionContext context = new HttpActionContext();
context.ControllerContext = controllerContext;
HttpAuthenticationContext m = new HttpAuthenticationContext(context, null);
HttpRequestHeaders headers = request.Headers;
AuthenticationHeaderValue authorization = new AuthenticationHeaderValue("scheme");
headers.Authorization = authorization;
You just simply need to create in ordinary fashion certain objects and pass them to other with constructors or properties. The reason why I created HttpControllerContext and HttpActionContext instances is because HttpAuthenticationContext.Request property has only get part - its value may be set through HttpControllerContext. Using the method above you might test your filter, however you cannot verify in the test if the certain properties of objects above where touched simply because they are not overridable - without that there is no possibility to track this.

I was able to use the answer from #mr100 to get me started in solving my problem which was unit testing a couple of IAuthorizationFilter implementations. In order to effectively unit test web api authorization you can't really use AuthorizationFilterAttribute and you have to apply a global filter that check for the presence of passive attributes on controllers/actions. Long story short, I expanded on the answer from #mr100 to include mocks for the controller/action descriptors that let you test with/without the presence of your attributes. By way of example I will include the simpler of the two filters I needed to unit test which forces HTTPS connections for specified controllers/actions (or globally if you want):
This is the attribute that is applied where ever you want to force an HTTPS connection, note that it doesn't do anything (it's passive):
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class HttpsRequiredAttribute : Attribute
{
public HttpsRequiredAttribute () { }
}
This is the filter that on every request checks to see if the attribute is present and if the connection is over HTTPS or not:
public class HttpsFilter : IAuthorizationFilter
{
public bool AllowMultiple => false;
public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
List<HttpsRequiredAttribute> action = actionContext.ActionDescriptor.GetCustomAttributes<HttpsRequiredAttribute>().ToList();
List<HttpsRequiredAttribute> controller = actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<HttpsRequiredAttribute>().ToList();
// if neither the controller or action have the HttpsRequiredAttribute then don't bother checking if connection is HTTPS
if (!action.Any() && !controller.Any())
return continuation();
// if HTTPS is required but the connection is not HTTPS return a 403 forbidden
if (!string.Equals(actionContext.Request.RequestUri.Scheme, "https", StringComparison.OrdinalIgnoreCase))
{
return Task.Factory.StartNew(() => new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
{
ReasonPhrase = "Https Required",
Content = new StringContent("Https Required")
});
}
return continuation();
}
}
And finally a test to prove it returns a status of 403 forbidden when https is required but not used (using a lot of #mr100's answer here):
[TestMethod]
public void HttpsFilter_Forbidden403_WithHttpWhenHttpsIsRequiredByAction()
{
HttpRequestMessage requestMessage = new HttpRequestMessage();
requestMessage.SetRequestContext(new HttpRequestContext());
requestMessage.RequestUri = new Uri("http://www.some-uri.com"); // note the http here (not https)
HttpControllerContext controllerContext = new HttpControllerContext();
controllerContext.Request = requestMessage;
Mock<HttpControllerDescriptor> controllerDescriptor = new Mock<HttpControllerDescriptor>();
controllerDescriptor.Setup(m => m.GetCustomAttributes<HttpsRequiredAttribute>()).Returns(new Collection<HttpsRequiredAttribute>()); // empty collection for controller
Mock<HttpActionDescriptor> actionDescriptor = new Mock<HttpActionDescriptor>();
actionDescriptor.Setup(m => m.GetCustomAttributes<HttpsRequiredAttribute>()).Returns(new Collection<HttpsRequiredAttribute>() { new HttpsRequiredAttribute() }); // collection has one attribute for action
actionDescriptor.Object.ControllerDescriptor = controllerDescriptor.Object;
HttpActionContext actionContext = new HttpActionContext();
actionContext.ControllerContext = controllerContext;
actionContext.ActionDescriptor = actionDescriptor.Object;
HttpAuthenticationContext authContext = new HttpAuthenticationContext(actionContext, null);
Func<Task<HttpResponseMessage>> continuation = () => Task.Factory.StartNew(() => new HttpResponseMessage() { StatusCode = HttpStatusCode.OK });
HttpsFilter filter = new HttpsFilter();
HttpResponseMessage response = filter.ExecuteAuthorizationFilterAsync(actionContext, new CancellationTokenSource().Token, continuation).Result;
Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode);
}

Related

Cannot mock the query string in HttpRequestBase for unit test

I have the following code that I need to unit test:
public ActionResult VerifyVoucherCode()
{
var model = new VerifyVoucherModel();
model.Voucher = Request.GetFirstQueryValue("token", "voucher");
The second line here is where my test fails. The code for this method is:
public static string GetFirstQueryValue(this HttpRequestBase request, params string[] keys)
{
return request.QueryString.GetFirstValue(keys);
}
My attempt to unit test this part of the method so far is:
var httpContext = CreateHttpContext();
var httpRequestBase = new HttpRequestWrapper(httpContext.Request);
var nameValueCollection = new NameValueCollection();
var controller = CreateMvcController<SignUpController>();
MockActivationCodeHelper(validationResult, controller);
For reference, the CreateHttpContext method is:
private HttpContext CreateHttpContext()
{
var httpContext = new HttpContext(
new HttpRequest("", "http://tempuri.org", "token=123"),
new HttpResponse(new StringWriter())
);
return httpContext;
}
It fails here return request.QueryString.GetFirstValue(keys); with a System Not Implemented exception for Query String. Any pointers here please?
Please take a look at flurl, while it may require some refactoring to your code, using it allows to construct http requests in a very handy and fluent way. Moreover, it makes testing your business logic that depends on calling external HTTP services extremely easy (event for edge cases)
The library has good documentation and his author is very responsive on stackoverflow.
Check out: https://flurl.dev/

What are the best practices in adding custom header fields for a .net web api call on Swagger?

I can see a lot of ways to do it online but most of them are messy, for me I was using these two ways
Using scopes, I did one for mobile and another one for the website
var webScope = apiDescription.ActionDescriptor.GetFilterPipeline()
.Select(filterInfo => filterInfo.Instance)
.OfType<WebAuthorize>()
.SelectMany(attr => attr.Roles.Split(','))
.Distinct();
var mobileScope = apiDescription.ActionDescriptor.GetFilterPipeline()
.Select(filterInfo => filterInfo.Instance)
.OfType<MobileAuthorize>()
.SelectMany(attr => attr.Roles.Split(','))
.Distinct();
And it worked because I had two different ways in authorizing the api calls, as you can see I had a Mobile Authorize and a Web Authorize so my api calls would look something like this:
[HttpGet]
[Route("something")]
[WebAuthorize(Code = PermissionCode, Type =PermissionType)]
public async Task<Dto> Getsomething()
{
return await unitOfWork.GetService<ISomething>().GetSomething();
}
Issues I face when using scopes is that all calls that have web authorize will share the same headers so for the special calls I used another way to add custom headers.
Using apiDescription.RelativePath, and I will check it if the relative path is equal to the api call I want to add that custom header, example:
[HttpPost]
[Route("rename")]
[InHouseAuthorize(Code = PermissionCode, Type =PermissionType)]
public async Task<HttpResponseMessage> RenameDevice()
{
HttpRequestMessage request = Request ?? new HttpRequestMessage();
String deviceName = request.Headers.GetValues("deviceName").FirstOrDefault();
String deviceGuid = request.Headers.GetValues("deviceGuid").FirstOrDefault();
await unitOfWork.GetService<IDeviceService>().RenameDevice(deviceGuid, deviceName);
await unitOfWork.Commit();
return new HttpResponseMessage(HttpStatusCode.OK);
}
And then I would add to the AddRequiredHeaderParameter.cs the following
if (apiDescription.RelativePath.Contains("device/rename"))
{
operation.parameters.Add(new Parameter
{
name = "deviceGuid",
#in = "header",
description = "Add the Device Guid",
type = "string",
required = false
});
operation.parameters.Add(new Parameter
{
name = "DeviceName",
#in = "header",
description = "Add the Device Name",
type = "string",
required = false
});
}
At first this was convenient and good enough fix but things are turning ugly as I'm adding a lot of calls that need custom headers and if the same URL have a Get and Post then it will even get uglier.
I am searching for the best way to deal with this issue.
It's possible to use attribute [FromHeader] for web methods parameters (or properties in a Model class) which should be sent in custom headers. Something like this:
[HttpGet]
public ActionResult Products([FromHeader(Name = "User-Identity")]string userIdentity)
For me it looks like the easiest solution. At least it works fine for ASP.NET Core 2.1 and Swashbuckle.AspNetCore 2.5.0.

How to disable urlencoding get-params in Refit?

I use Refit for RestAPI.
I need create query strings same api/item?c[]=14&c[]=74
In refit interface I created method
[Get("/item")]
Task<TendersResponse> GetTenders([AliasAs("c")]List<string> categories=null);
And create CustomParameterFormatter
string query = string.Join("&c[]=", values);
CustomParameterFormatter generated string 14&c[]=74
But Refit encoded parameter and generated url api/item?c%5B%5D=14%26c%5B%5D%3D74
How disable this feature?
First of all was your api server able to parse the follow?
api/item?c%5B%5D=14%26c%5B%5D%3D74
Encoding is great for avoiding code injection to your server.
This is something Refit is a bit opinionated about, i.e uris should be encoded, the server should be upgraded to read encoded uris.
But this clearly should be a opt-in settings in Refit but it´s not.
So you can currently can do that by using a DelegatingHandler:
/// <summary>
/// Makes sure the query string of an <see cref="System.Uri"/>
/// </summary>
public class UriQueryUnescapingHandler : DelegatingHandler
{
public UriQueryUnescapingHandler()
: base(new HttpClientHandler()) { }
public UriQueryUnescapingHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{ }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var uri = request.RequestUri;
//You could also simply unescape the whole uri.OriginalString
//but i don´t recommend that, i.e only fix what´s broken
var unescapedQuery = Uri.UnescapeDataString(uri.Query);
var userInfo = string.IsNullOrWhiteSpace(uri.UserInfo) ? "" : $"{uri.UserInfo}#";
var scheme = string.IsNullOrWhiteSpace(uri.Scheme) ? "" : $"{uri.Scheme}://";
request.RequestUri = new Uri($"{scheme}{userInfo}{uri.Authority}{uri.AbsolutePath}{unescapedQuery}{uri.Fragment}");
return base.SendAsync(request, cancellationToken);
}
}
Refit.RestService.For<IYourService>(new HttpClient(new UriQueryUnescapingHandler()))
For anyone who stumbles across this old question you can use [QueryUriFormat(UriFormat.Unescaped)] attribute.
🙋‍♀️ In my case I had to some work on a legacy .NET 2.2 Core ASP app (i.e. couldn't use the fancy attribute mentioned in mmoon's answer.
To complete Ahmed Alejo's answer, and in particular if you're taking the ASP.NET Core DI container road, I've noticed that if I left the two ctors as they were in the original answer, the whole thing was kabooming (i.e. getting the infamous exception: The 'DelegatingHandler' list is invalid because the property 'InnerHandler' of 'handler' is not null).
So what I ended up doing was to not write any ctors, just as below 👇
public class UriQueryUnescapingHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var uri = request.RequestUri;
var unescapedQuery = Uri.UnescapeDataString(uri.Query);
var userInfo = string.IsNullOrWhiteSpace(uri.UserInfo) ? string.Empty : $"{uri.UserInfo}#";
var scheme = string.IsNullOrWhiteSpace(uri.Scheme) ? string.Empty : $"{uri.Scheme}://";
request.RequestUri = new Uri($"{scheme}{userInfo}{uri.Authority}{uri.AbsolutePath}{unescapedQuery}{uri.Fragment}");
return base.SendAsync(request, cancellationToken);
}
}
and then it just works like a charm when you're configuring the services that way 👇:
services.AddTransient<UriQueryUnescapingHandler>();
// whatever other services you need to set up beforehand
services
.AddRefitClient<IMyRefitService>(myRefitSettings)
.ConfigureHttpClient(c =>
{
// whatever relevant HttpClient-related configuration action goes here
})
.AddHttpMessageHandler<UriQueryUnescapingHandler>();

Unit test dependencies for an ASP.NET MVC 3 ViewResult subclass

I'm creating a really simple ViewResult subclass called JavaScriptViewResult that, when executing, calls the base implementation and then sets the Content-Type of the response to text/javascript. In trying to unit test this class, I'm running across a slew of difficulties fulfilling all of the dependencies of the ASP.NET MVC stack.
Here is what my unit test, which uses Rhino, looks like so far:
[TestMethod]
public void TestExecuteAction()
{
var request = MockRepository.GenerateMock<HttpRequestBase>();
request.Expect(m => m.Url).Return(new Uri("/Test/JavaScript", UriKind.Relative));
var httpContext = MockRepository.GenerateMock<HttpContextBase>();
httpContext.Expect(m => m.Request).Return(request);
var controller = MockRepository.GenerateMock<ControllerBase>();
var virtualPathProvider = MockRepository.GenerateMock<VirtualPathProvider>();
var routeCollection = new RouteCollection(virtualPathProvider);
routeCollection.MapRoute("FakeRoute", "Test/JavaScript", new { controller = "Test", action = "JavaScript" });
var routeData = routeCollection.GetRouteData(httpContext);
var context = new ControllerContext(httpContext, routeData, controller);
var viewResult = new JavaScriptViewResult();
viewResult.ExecuteResult(context);
Assert.AreEqual("text/javascript", context.HttpContext.Response.ContentType);
}
The latest exception when running the test is a NullReferenceException deep within the bowels of System.Web.Routing.Route.GetRouteData(HttpContextBase httpContext).
How do I set up all of the dependencies for executing a ViewResult? Are there any techniques for making this simpler? Alternately, is there a different way I can utilize the MVC view engine to generate JavaScript that will set the proper Content-Type for the response?
I figured out how to meet the minimum requirements of ViewResult. One problem I was encountering was mocking the process of finding the view. This was avoidable by ensuring that the View property of my object was populated. Here is my working test:
[TestMethod]
public void TestExecuteAction()
{
var response = MockRepository.GenerateStub<HttpResponseBase>();
response.Output = new StringWriter();
var httpContext = MockRepository.GenerateMock<HttpContextBase>();
httpContext.Expect(m => m.Response).Return(response);
var routeData = new RouteData();
routeData.Values.Add("action", "FakeAction");
var context = new ControllerContext(httpContext, routeData, MockRepository.GenerateMock<ControllerBase>());
var viewResult = new JavaScriptViewResult();
viewResult.View = MockRepository.GenerateMock<IView>();
viewResult.ExecuteResult(context);
Assert.AreEqual("text/javascript", context.HttpContext.Response.ContentType);
}

How do I test ActionFilterAttributes that work with ModelState?

As suggested by (among others) Kazi Manzur Rashid in this blog post, I am using ActionFilterAttributes to transfer model state from one request to another when redirecting.
However, I find myself unable to write a unit test that test the behavior of these attributes. As an example, this what I want the test for the ImportModelStateAttribute to do:
Setup the filterContext so that TempData[myKey] contains some fake "exported" ModelState (that is, a ModelStateDictionary I create myself, and add one error to)
Make ModelState contain one model error.
Call OnActionExecuting.
Verify the two dictionaries are merged, and ModelState now contains both errors.
I'm at a loss already on the first step.
EDIT:
Yes, I've tried mocking ActionFilterAttribute with Moq, but I get errors stating
Invalid setup on non-overridable member
for both TempData and ModelState.
Tomas, You do not have to mock the filterContext, you can create the real object for testing the action filter, the same goes for the model state, these are poco objects. Only thing that you have to mock is the HttpContext (if needed).
[Fact]
public void Should_import_complete_view_data()
{
var attribute = new ImportViewDataFromTempDataAttribute();
var httpContext = new Mock<HttpContextBase>();
var requestContext = new RequestContext(httpContext.Object, new RouteData());
var previousModel = new object();
var previousViewData = new ViewDataDictionary(previousModel) {{"foo", "bar"}};
previousViewData.ModelState.AddModelError("foo", "bar");
var controller = new Mock<ControllerBase>();
controller.Object.ViewData = new ViewDataDictionary();
controller.Object.TempData = new TempDataDictionary { { attribute.Key, previousViewData } };
var controllerContext = new ControllerContext(requestContext, controller.Object);
var actionContext = new ActionExecutingContext(controllerContext, new Mock<ActionDescriptor>().Object, new Dictionary<string, object>());
attribute.OnActionExecuting(actionContext);
Assert.True(actionContext.Controller.ViewData.ContainsKey("foo"));
Assert.True(actionContext.Controller.ViewData.ModelState.ContainsKey("foo"));
Assert.Same(previousModel, actionContext.Controller.ViewData.Model);
}

Categories