How to test Filters that verify Claims data?

Asked

Viewed 155 times

6

I own a ActionFilter for some controllers and actions that make use of the user information that are in Claims.

The filter itself obtains information from Claims user and executes the authorization validation process:

public void OnAuthorization(AuthorizationContext filterContext)
{
    if (Perfil >= Perfil.Administrativo)
        return;

    var cookie = new CookieInfo(HttpContext.Current.User.Identity as ClaimsIdentity);

    if (Perfil == Perfil.Usuario)
    {
        filterContext.Result = new RedirectResult("/Home/Index");
        filterContext.Controller.TempData["ErrorMessage"] = "Acesso inválido!";
        return;
    }

    if (Perfil <= Perfil.Suporte)
    {
        filterContext.Result = new RedirectResult("/Home/Index");
        filterContext.Controller.TempData["ErrorMessage"] = "Acesso inválido!";
    }
    ...
}

How to write/create test methods to test filter functionality?

3 answers

3

Write a method that instantiates the execution context (which in turn instantiates the authorization attribute, which is a filter) and makes a mock of the request, more or less like this:

public void TesteDoFiltroDeAutorizacao()
{
    var context = new ActionExecutedContext();
    var httpRequest = new HttpRequest("", "http://umenderecoqualquer/", "");
    var stringWriter = new StringWriter();
    var httpResponse = new HttpResponse(stringWriter);
    context.HttpContext = new ControllerContext(new HttpContextBase { Request = httpRequest, Response = httpResponse }, 
              new RouteData(), 
              new QualquerController());
    var filter = new MinhaAutorizacaoAttribute(/* Aqui acho que você passa um perfil, pelo que eu entendi */);

    filter.OnActionExecuted(context);

    Assert.True(/* Teste aqui context.Result */);
}
  • I know it’s just an example, but I could see the line context.HttpContext = new HttpContext(httpRequest, httpResponse); ? The types are different. I believe I will need to pass values to the Main, as in the question how-to-a-controller-Asp-net-mvc-using-Moq-e-entity-framework. Can I somehow get through by going down this path? Thankful!

  • True, I switched the answer to ControllerContext. ControllerContext has a property called User, in which you can use to mock IPrincipal.

  • the new HttpContext(httpRequest, httpResponse) passed to the new ControllerContext are of different types.

  • I updated to Httpcontextbase.

1

How I resolved:

protected static ActionExecutedContext FakeExecutedContext(IPrincipal principal = null, 
    Controller controller = null)
{
    var request = new Mock<HttpRequestBase>();
    request.SetupGet(x => x.HttpMethod).Returns("GET");
    request.SetupGet(x => x.Url).Returns(new Uri("http://example.com/action"));

    var httpContext = new Mock<HttpContextBase>();
    httpContext.SetupGet(x => x.Request).Returns(request.Object);

    // aqui adiciono o principal quando passado por parâmetro
    if (principal != null)
        httpContext.SetupGet(x => x.User).Returns(principal);

    var executedContext = new Mock<ActionExecutedContext>();
    executedContext.SetupGet(x => x.HttpContext).Returns(httpContext.Object);
    if (controller != null)
        executedContext.SetupGet(x => x.Controller).Returns(controller);

    return executedContext.Object;
}

Simplifying the test method for this:

[TestMethod]
public void TentativaDeAcessoComPerfilInferiorAoSolicitadoPeloFiltro()
{
    // variáveis
    // Uma claim será setada com nível de acesso de Suporte
    var identity = new ClaimsIdentity(new List<Claim>
    {
        new Claim(CustomClaimTypes.Perfil, Perfil.Suporte.ToString())
    });

    var principal = new GenericPrincipal(identity, new[] { "" });

    // A simulação requer um nível de acesso Administrativo
    var filter = new PerfilFilterAttribute(Perfil.Administrativo, Context);
    var fakeExecutedContext = FakeExecutedContext(principal, new HomeController());

    // execução
    filter.OnActionExecuted(fakeExecutedContext);

    // validação
    // O resultado deve apontar um redirecionamento para o /Home/Index
    Assert.IsNotNull(fakeExecutedContext.Controller.TempData["ErrorMessage"]);
    Assert.IsNotNull(fakeExecutedContext.Result as RedirectResult);
    Assert.AreEqual(((RedirectResult)fakeExecutedContext.Result).Url, "/Home/Index");
}

There’s a detail on the filter, which has been changed from:

var cookie = new CookieInfo(HttpContext.Current.User.Identity as ClaimsIdentity);

To:

var cookie = new CookieInfo(filterContext.HttpContext.User.Identity as ClaimsIdentity);

0

You can foreach your saved Claims as follows:

IEnumerable<System.Security.Claims.Claim> Claims = HttpContext.GetOwinContext().Authentication.User.Claims;
foreach (var t in Claims)
{
    if(t.Type.Equals("http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid"))
        idUsuario = t.Value;
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.