Accessing ApiController Type in DelegatingHandler - c#

I am using Asp.Net WebAPI for a project. I am currently working on authentication and authorization.
I have a messageHandler that will check the HTTP authentication header of a request and build my identity and user profile. However, I want to annotate my controller action (or just the controller) with claims that the action may require (we have a lot of claims that a user can have, so I don't want to load them all).
e.g.:
public class MyController : ApiController
{
[LoadClaims("SomeClaim", "SomeOtherClaim", "etc")]
public string Get()
{
if (HasClaim("SomeClaim"))
return "Awesome";
return "Bummer";
}
}
Inside the authentication message handler I want to be able to look at the attributes and bring claims back from the DB based on only what is required. For that I need to know what Controller and Action I will hit based on route:
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
...
var routeData = request.GetRouteData();
object controllerName;
object actionName;
routeData.Values.TryGetValue("controller", out controllerName);
...
So I can get that. But now I need to turn this into a Type that I can reflect on, but all I have is the controller name (not even the full class name or namespace). How can I turn this into something that I can reflect on to get attributes etc?
I am looking into DefaultHttpControllerSelector to see how the WebAPI stack does it, and it seems to use HttpControllerTypeCache. This is an internal class so I can't create an instance. What is the correct way to go about getting the target controller Type?

You can get access to the type resolver yourself using the global service locator.
var controllerTypeResolver = GlobalConfiguration.Configuration.Services.GetHttpControllerTypeResolver();
var controllerTypes = controllerTypeResolver.GetControllerTypes(GlobalConfiguration.Configuration.Services.GetAssembliesResolver());
var controllerType = controllerTypes.SingleOrDefault(ct => ct.Name == string.Format("{0}Controller", controllerName));
You will probably want to do some caching of the results (like the controller selector does). But this approach should work.
But
You may be better off moving this logic into a Custom authorisation filter that sits on the controller rather than a delegating handler. Given you need to know the controller type you may as well let the ControllerSelector work normally. Perhaps, if you turned your load claims attribute into an authorization action filter attribute you could just load the claims passed in as parameters and set the principal and claims there and then?

If you are still set on the DelegatingHandler you could get the Controller Selector instance itself, which'll be way more efficient:
var controllerSelector = GlobalConfiguration.Configuration.Services.GetHttpControllerSelector();
var controllerDescriptor = controllerSelector.SelectController( request );

Related

ASP.NET Core 2.2 MVC : wrap responses

I have an application developed in ASP.NET Core MVC with a set of controllers for normal view responses and Web API.
I am trying to figure a correct way to wrap all Web API responses with a consistent class.
My first question is what would be a correct approach to wrap the responses coming from Web API controllers. Since I have two controller types, I would need to distinguish between them as the responses should only be wrapped for API controller, and not view controllers.
As I understand there are two choices a middleware or an action filter.
At first I thought the middleware would be the best choice, but then I realized that I would still need to figure out what kind of request is being processed which would add more potential headache with maintenance?
Then I looked at an action filter and it seems that it would be a better choice to plugin the wrapping handling.
For example an action filter can be added to a base controller just for Web API and not controllers handling the views.
So the question is whether the action filters are best approach to achieve this?
I would recommend you to look at result filters for this. Result filters run after a controller action has produced a result, and it allows you to look at that result and then perform some action.
For example, when you return View in a controller action, then what gets returned is a ViewResult which makes it easy to identify results that would cause a Razor view to be rendered.
Within an API controller, you would usually return a ActionResult<T>, some model object directly, or an ObjectResult. The former two will be automatically converted into an ObjectResult as the action gets executed. So you can just look for ObjectResults and then change the result to have some wrapper object instead. This would look something like this:
public class WrapApiResponseResultFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
var result = context.Result;
if (result is ObjectResult)
{
// wrap the inner object
var newValue = new WrapperObject(result.Value);
// replace the result
context.Result = new ObjectResult(newValue)
{
// copy the status code
StatusCode = result.StatusCode,
};
}
}
public void OnResultExecuted(ResultExecutedContext context)
{ }
}

Injecting route values based on the current Principal

I've got a web api that accepts authentication through either an api key, a user access token, or a client access token. I've already written a DelegatingHandler for each case, that creates a new ClaimsPrincipal based on the given authentication details, and have confirmed that the principal is accessible within a controller action.
What I want to do now is inject either a company, a user, or a publisher into the route values, so I can create overloads on the controller for each case. What class/interface do I need to extend in order to plug into the pipeline with the following conditions:
Has access to the current principal
Has access to the route data
The action has not yet been selected
Edit
I'm not looking to sidestep routing here - I still want MVC to choose a route for me, based on the route values. I just want to add one more parameter to the route values before it chooses, and the parameter will have a different name and type depending on whether a user access token is used, or an api key is used.
I believe the subject of authentication should result in two distinct methods, because an api key can access all resources for a company, while a user access token can only access the resources they have been given permission to view.
I do not see a reason why you would want to go with a controller here. You would be sidestepping routing, a very opinionated piece of MVC. I would create middleware that runs before MVC (which is, itself, just middleware) instead.
If you're looking to affect RouteData inline, I would look into using a global IResourceFilter or IAsyncResourceFilter. Then, you can update the RouteData property on the ResourceExecutingContext based upon the conditions you specified in your question.
Any additional dependencies you need to determine how to populate the RouteData property can be injected into the resource filter's constructor as specified in the section on dependency injection.
public class SetRouteValueResourceFilter : IAsyncResourceFilter {
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) {
var company = context.HttpContext.Identity.FindFirst("Company")?.Value;
if (company != null) {
context.RouteData.Values.Add("company", company);
}
await next();
}
}
This answer is just an idea. Even though this is handled before the action logic, I'm not sure it will affect which route is selected. According to this diagram, routes are selected before filters are ran.
I managed to get this going by making a custom ValueProviderFactory, which reads values from the current principal's claims and makes them available for parameter binding:
public class ClaimsPrincipalValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(HttpActionContext actionContext)
{
if (actionContext.RequestContext.Principal != null && actionContext.RequestContext.Principal is ClaimsPrincipal principal)
{
var pairs = principal.Claims.Select(claim => new KeyValuePair<string, string>(claim.Type, claim.Value));
return new NameValuePairsValueProvider(pairs, CultureInfo.CurrentCulture);
}
return null;
}
}
In order to use it, you can annotate the input parameters with the ValueProvider attribute:
public class FooController : ApiController
{
[HttpGet]
public void Bar([ValueProvider(typeof(ClaimsPrincipalValueProviderFactory))]ApiKey apiKey)
{
// ...
}
}
That's pretty ugly and unreadable, what I really wanted was something like the FromUri attribute, but for claims. ValueProviderAttribute and FromUriAttribute both inherit from ModelBinderAttribute, so I created a class which does the same:
/// <summary>
/// The action parameter comes from the user's claims, if the user is authorized.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class FromClaimsAttribute : ModelBinderAttribute
{
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
return parameter.BindWithModelBinding(new ClaimsPrincipalValueProviderFactory());
}
}
Now the example method on the FooController looks a lot more readable:
[HttpGet]
public void Bar([FromClaims]ApiKey apiKey)
{
// ...
}
Update
Looks like this is still having problems with route selection and overloading, especially when some of the parameters are nullable and the value is null. I'm going to have to keep looking into this.
Update #2
I managed to simplify the value provider stuff a lot, after finding there is a built-in NameValuePairValueProvider

Claims athorization service API usability

Is the following API of a claims authorization service ok, from the point of view of usability?
/* before UPDATE it was like this:
var canEdit = Authz.ForNewRequest()
.WithActionName("edit")
.WithResourceName("project")
.WithResourceClaim("DepartmentId","21")
.CheckAccess(); */
//UPDATE:
var deptId = SomehowGetDepartmentIdAtRuntime(targetProject);
var canEdit = Authz.ForNewRequest()
.WithActionName("edit")
.WithResourceName("project")
.WithResourceClaim("DepartmentId",deptId)
.CheckAccess();
if (canEdit)
{
//edit project
}
And configuration like this:
var authorizationModel = Authorization.ConfigNamespace.AuthzConfig
.LoadAuthorizationModelFromXml("authz.xml");
Authorization.ConfigNamespace.AuthzConfig
.SetApplicationAuthorization(authorizationModel);
Or custom configuration like this:
var authzCustomConfig = Authorization.ConfigNamespace.AuthzConfig
.NewCustomConfiguration()
.WithCustomClaimBasedFactFunctions(claimBasedFunctions)
.WithCustomClaimProviders(claimProviders)
.WithCustomCompositeFactFunctions(compositeFactFunctions)
.WithCustomObligations(obligations);
var authorizationModel = Authorization.ConfigNamespace.AuthzConfig
.LoadAuthorizationModelFromXml("authz.xml", authzCustomConfig);
Authorization.ConfigNamespace.AuthzConfig
.SetApplicationAuthorization(authorizationModel);
Basically, the question is about the top part of the iceberg, i.e. how to use the service, but not how to implement or design the inner part. But just in case, here are a couple of general words about this service:
This service gives an answer of true/false for the given authorization request.
Authorization request has information about:
Action (action name, for example)
Resource (resource name, resource properties)
Subject (user name, user id, user roles, user properties)
Due to the fact that i use Microsoft.IdentityModel:
The properties of an action, resource, or a subject are presented as Claims. Approximately, a claim is an object, which has a value, value name, and value type. For example, a claim for a user could have the following info: ("UserName", "Andrey", "string").
The authorization request is the AuthorizationContext class from the Microsoft.IdentityModel.Claims namespace.
Two more things to consider:
I'd like this service to be a general solution, which could fit not only to one certain project.
Logic could be involved in order to understand if a condition for the permission decision is met or not.
That's why custom logic might be injected. Those claimBasedFunctions, claimProviders, compositeFactFunctions, and obligations are the custom logic. But they don't really matter for the question, just some custom confuguration elements, implementations of the interfaces, which are defined in the authorization assembly. The question is not about what they should be, or how they should work. You can think of them as of any interface implementatons that have to be injected.
Thanks!
P.S. this question is off-topic for the Code Review site.
If I interpret your description correctly, the following code means: "If you want to edit a project, you need a claim with name DepartmentId that has a value of 21.
var canEdit = Authz.ForNewRequest()
.WithActionName("edit")
.WithResourceName("project")
.WithResourceClaim("DepartmentId","21")
.CheckAccess();
if (canEdit)
{
//edit project
}
This statement would be at the start of your Edit action in your Project controller in case you were building an MVC application.
If my interpretation is correct, I would advise you to remove the WithActionName and WithResourceName methods. Those things can be retrieved from the context in which this code is executing. Your fluent API is too easy to copy from one method to another and forget to update those strings. I would look at a custom authorize attribute that you attach to an action in which you checks the claims.
UPDATE:
I was thinking something like this:
public class ProjectController : ApiController
{
[HttpPost]
[MyAuthorize("DepartmentId","21")
public void Edit(string applicationName)
{
// business logic
}
}
Inside the MyAuthorize attribute implementation you can retrieve the controller- and action names. If the developer using the attribute doesn't have to specify this, he/she can't get it wrong.

Resource based ASP.NET Authorization FormsAuthentication and WebApi

Using webapi with formsauthentication (i know may sound weird, but it exactly what I need).
I have a route like :
[Route("{application}/orders}"]
public IHttpActionResult Get(string application) {
var orders = OrderService.GetOrders(application); // return list of orders of applicaiton.
return Ok(orders);
}
Example scenario :
User John has permission to see orders for application "CoolApp"
User Leia has permission to see orders for application "Robin"
user Tristan doesn't have permission to see orders.
On the DB is like a Role - Resource relationship.
UserRole:
User - Role - Application
=====================================
John - OrdersManager - CoolApp
Leia - OrdersManager - Robin
Tristan - OtherRole - Robin
I want that route to check if the user has permission to get orders of that specific application.
I read this: Resource based authorization in .net and looks promising.
First thing is I'm not sure if I should extend using AuthorizeAttribute, AuthorizationFilterAttribute or IAuthorizationFilter as briefly explained here http://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api unfortunately, is not of much information.
Second I read that binding happens AFTER the authorization so I don't know how to get the values from the RouteData since I have the {application} defined on the route data I guess I should be able to access it. There are few examples for MVC, but was not able to get one for web api and with object like request and context all over the actionContext object I'm not sure where to get it and how in a proper way.
And last but not least, what method of the chosen extension point(s) should I override. If it have both async and sync, should I override both ?
BONUS: Since the authorize will have a dependency on my repository, would be great to know how to inject that into the filter if that's even possible. (I'm not using any IoC container).
Overriding AuthorizationFilterAttribute would probably be the better route. What you'd want to do is pass the attributes you want to check for:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
string role;
string application
public MyAuthorizeAttribute (string role, string application){
this. role = roles;
this.application = application;
}
protected override bool IsAuthorized(HttpActionContext actionContext) {
var routeData = actionContext.ControllerContext.RouteData;
//do your checks
}
}
you can get access to routedata from the HttpActionContext in the IsAuthorized method. Now you can attach the attribute to your ApiController or Action Method.
I'd suggest using an IoC container is you want to do DI with an Web API ActionFilter

Multi-tenant application allow one user to access many tenant accounts?

I'm writing a multi-tenant application using a one database per tenant model. I have allowed each user account to access multiple tenants (as long as that tenant has given them access)
Each page sent to the browser includes the current TenantId in Site.Master
<%= Html.Hidden("TenantId") %>
But when any request is made from the browser (submit button, AJAX GET or AJAX POST), this TenantId is NOT actually checked to see if it matches the user's current TenantId.
Now if the user opens one tab, with TenantId = 1, then connects to another tenant in another tab with TenantId = 2, then switches back to the first tab and it has access to data from Tenant 2.
What can I do to fix this? I have a large number of existing ActionResult and JsonResult methods and I do not want to go through every one of them and add
if (request.TenantId != user.CurrentTenantId) return false
Because that would be a large amount of duplicated effort
Can I change my base controller to always read the value of TenantId? That could work for submitted requests (ActionResult), but what about AJAX requests?
How can I check the TenantId of the page inside of JsonResult actions without changing every single existing AJAX method (there are a lot of them)?
You could check your in the Application_Request event in the Global.asax.cs file. If what you need is populated via MVC model binding, then maybe write a custom ActionFilter to check it and register it with all actions via a GlobalFilter.
If I understood correctly, users could have simultaneously open 2 different tabs, each with a different tenant. And each page should display data relevant to each tenant.
So that means a solution involving a cookie or the session needs to be discarded as the tenant is specific to each browser tab.
And reading your answer to Cyril Gupta´s suggestion, I understand the hidden tenantId on each page may not be submitted on every AJAX request.
Of course, one solution could be to modify your application and make sure this is always the case with every AJAX request.
Otherwise that would also discard a global filter based on the request parameters as the tenantId may not always be there.
I think the best option then is to add a segment in the URL holding the tenantId.
For example replacing the default route with something like the following route (If you have many different routes you would need to be very careful to avoid route collision):
routes.MapRoute(
name: "Default",
url: "{tenant}/{controller}/{action}/{id}",
defaults: new { tenant = "defaultTenant", controller = "Home", action = "Index", id = UrlParameter.Optional }
);
This way you can make sure the tenant will always be submitted on each request, and you could also have 2 different tabs with different tenants displaying each the appropriate data.
There are different options on how to recover the value of the route segment.
The binding will automatically populate the value on any parameter named "tenant" on your action method, or any parameter named "tenant" in a model class that is a parameter of the action method:
public ActionResult Foo(FooModel model, string tenant)
{
//both tenant and model.tenant will contain the value of the URL segment
return View();
}
You could also write a filter that access the value of the route parameter (RouteData is a property of the ActionExecutingContext and ActionExecutedContext class received as parameters of the filter methods), and performs some logic. The filter would then be set as a global filter in your application or to your base controller:
public class FooFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var tenant = filterContext.RouteData.Values["tenant"]
//do whatever you need to do before executing the action, based on the tenant
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var tenant = filterContext.RouteData.Values["tenant"]
//do whatever you need to do after executing the action, based on the tenant
}
}
The final option is to directly access the RouteData parameter on your base controller class. (As RouteData is a property of the base MVC Controller class)
As long as you are using Html and Ajax helpers for URL generation, the tenant segment of the URL will be maintained in your links. However if you have jquery code directly sending AJAX calls with hardcoded URLs, you would then need to update that code so the new url segment is taken into account.
Finally, in case the tenantId values are something not very user friendly like an integer, you could have unique names for each tenant and use the names in the URL. You would then add some logic that maps it to the integer value your application need.
You can write you own filter:
How do I get certain code to execute before every single controller action in ASP.NET MVC 2?
Executing code before any action
Of course there is no ready answer for you question. You need to write you own logic, how to handle tenantId. For example on each action, check if it doesnt equal as current session tenant id make redirect. Or put it in cookie and check every time in filter whether id's are equal. It's up to you. From my point of view cookie is more preferrable. But it eat traffic.
You can apply a filter at the controller level and check the tenantid that is being sent. Controller level filters shouldn't be any more difficult than action filters. For my project I needed to check authorisation in a similar manner but I overrode the controller class and then inherited from my own controller class as I had some very special needs.
Where are you going to store the tenant Id on the client side? It seems to me that you should be using a session object to do this.

Categories