Ignore controller in ASP.NET Web API - c#

Our team maintains a self-hosted ASP.NET Web API. The project uses attribute routing and we have dozens of existing controllers. Lets say, the API is exposed via the main path /api/purpose1/... with all the existing controllers being placed as resources underneath.
Now I want to introduce a new parallel main path, e. g. /api/purpose2/. It should be possible to activate both main paths independently of each other via a boolean variable in a config file.
Since all the controllers are within one assembly, the attribute routing approach always finds and adds them to both purpose1 and purpose2. This contradicts the independency of purpose1 and purpose2. So I used attribute routing for purpose1 and convention-based routing for purpose2. That at least worked, but I'm not happy with the mixture of two different routing approaches.
So my question is: can I disable certain controller classes with attribute routing?

OnActionExecuting example:
V1 controller
[Route("api/[controller]")]
[ApiController]
public class SampleV1Controller : VersioningAwareControllerBase
{
[HttpGet]
public IActionResult Get()
{
return new OkObjectResult("V1");
}
}
V2 controller
[Route("api/[controller]")]
[ApiController]
public class SampleV2Controller : VersioningAwareControllerBase
{
[HttpGet]
public IActionResult Get()
{
return new OkObjectResult("V2");
}
}
Versioning aware base controller
public abstract class VersioningAwareControllerBase: ControllerBase, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
if (!FeatureFlags.ShouldDeprecateV1 ||
!string.Equals(context.RouteData.Values["controller"].ToString(), "samplev1",
StringComparison.OrdinalIgnoreCase))
return;
context.Result = NotFound();
context.Canceled = true;
}
public void OnActionExecuting(ActionExecutingContext context) { }
}

Peter Csala's answer is fine, however, it has a dependency to System.Web.Mvc. In our case, this dependency wasn't there before and I found a solution that does not require adding it.
I've extended ApiControllerActionInvoker the following way:
internal class CustomHttpActionInvoker : ApiControllerActionInvoker
{
public CustomHttpActionInvoker(IConfigProvider configProvider)
{
ConfigProvider = configProvider;
InvokeActionFunc = base.InvokeActionAsync;
}
/// <summary>FOR AUTOMATED TESTS ONLY</summary>
internal CustomHttpActionInvoker(IConfigProvider configProvider,
Func<HttpActionContext, CancellationToken, Task<HttpResponseMessage>> invokeActionFunc)
{
ConfigProvider = configProvider;
InvokeActionFunc = invokeActionFunc;
}
private IConfigProvider ConfigProvider { get; }
private Func<HttpActionContext, CancellationToken, Task<HttpResponseMessage>> InvokeActionFunc { get; }
/// <inheritdoc />
public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
var isRelevantRequest = actionContext.ControllerContext.Controller is MyRelevantController;
if (isRelevantRequest && ConfigProvider.IsPurpose1)
{
return InvokeActionFunc(actionContext, cancellationToken);
}
if (!isRelevantRequest && ConfigProvider.IsPurpose2)
{
return InvokeActionFunc(actionContext, cancellationToken);
}
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
The internal constructor was introduced to support easier unit testing.
The following code registers the custom class:
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Services.Replace(typeof(IHttpActionInvoker), new CustomHttpActionInvoker(MyConfigProvider));

Related

.NET Core Set ClaimsPrincipal from another controller

In few places in legacy code (more than 100 controllers), we are running action from other controllers.
In .NET Framework it runs OK - ClaimsPrincipal in both controller's action have correct values, but in .NET Core, running SecondController.internalPut() from FirstController gives me NullReferenceException.
FirstController:
[EnableCors]
public class FirstController : BaseApiController
{
public FirstController(IContextFactory contextFactory) : base(contextFactory)
{
}
[HttpPut]
[HttpPost]
[Route("/api/firstcontroller")]
public IActionResult Put([FromBody] MyDTO data)
{
var token = Identity.Token; // <--- correct value
var secondController = new SecondController(ContextFactory);
secondController.internalPut(something); <--- NullReferenceException
return Ok();
}
}
SecondController:
[EnableCors]
public class SecondController : BaseApiController
{
public SecondController(IContextFactory contextFactory) : base(contextFactory)
{
}
[HttpPut]
[HttpPost]
public async Task<IActionResult> Put(Guid myGuid)
{
internalPut(something); // <-- OK
return Ok();
}
internal void internalPut(object something)
{
var token = Identity.Token; // <--- NullReferenceException when running from FirstController!!
}
}
And BaseApiController with TokenIdentity:
[ApiController]
[Route("/api/[controller]")]
[Route("/api/[controller]/[action]")]
public class BaseApiController : ControllerBase
{
protected readonly IMyContextFactory ContextFactory;
public BaseApiController(IMyContextFactory contextFactory)
{
ContextFactory = contextFactory;
}
public TokenIdentity Identity => User?.Identity as TokenIdentity;
}
public class TokenIdentity : GenericIdentity
{
public Guid Token { get; set; }
public string User { get; set; }
public TokenIdentity(Guid token) : base(token.ToString())
{
Token = token;
}
}
How is the easiest fix for this bug? I know that I can change BaseApiController implementation to get ClaimsPrincipal from IHttpContextAccessor, but this means that I need to update constructors for all > 100 controllers in code...
It is another way to always have ClaimsPrincipal when we are calling action from another controller?
What I recommend as the correct solution
I can't emphasise enough how much I recommend moving shared functionality into its own services, or perhaps look at using the Mediator Pattern (e.g. using the MediatR library) to decouple your controllers from their functionality a little. What I provide below is not a solution, but a band-aid.
What I recommend a QUICK FIX only
Why is this only a quick fix?: because this doesn't instantiate the correct action details and route parameters, so it could potentially cause you some hard-to-find bugs, weird behaviour, URLs maybe not generating correctly (if you use this), etc.
Why am I recommending it?: because I know that sometimes time is not on our side and that perhaps you need a quick fix to get this working while you work on a better solution.
Hacky quick fix
You could add the following method to your base controller class:
private TController CreateController<TController>() where TController: ControllerBase
{
var actionDescriptor = new ControllerActionDescriptor()
{
ControllerTypeInfo = typeof(TController).GetTypeInfo()
};
var controllerFactory = this.HttpContext.RequestServices.GetRequiredService<IControllerFactoryProvider>().CreateControllerFactory(actionDescriptor);
return controllerFactory(this.ControllerContext) as TController;
}
Then instead of var secondController = new SecondController(ContextFactory); you would write:
var secondController = CreateController<SecondController>();

How to write a custom precheck middleware for some of the endpoints?

I have a asp dotnetcore web service that exposes some endpoints. For some of the endpoints, I want to run a check if existing scorecard is visible. The endpoints urls are:
GET /api/v1/scorecard/{ScorecardId}/details
GET /api/v1/scorecard/{ScorecardId}/rankings
These endpoints are just examples but they could be tens in numbers. Each of these endpoints have their own handlers like:
public async Task<ScorecardDetails> Get(long scorecardId)
{}
public async Task<ScorecardRankings> Get(long scorecardId)
{}
In database, there is a table Scorecard that stores the scorecard details and has a column IsVisible. I want to return 404 for all calls to these scorecard endpoints for scorecards that are set IsVisible = False in database.
I think you should consider using IActionFilter and IAsyncActionFilter for this purpose. In there you have a chance to read the already bound-model for parameters to better validate it. Of course that way has its own complexity unless you accept the way in which we decorate every parameters on every methods that require to check the existence of objects. That way is fairly inconvenient but to make it convenient, you need to design a model to allow you to declare (setup or configure) your targeted endpoints as well as how to target the required parameters for the existence checking process.
Here I introduce the way of using a middleware, just like what you want originally. It sounds more convenient than using action filters but it has its own complexity and inconvenience. At the phase of the middleware, we don't have any data bound to parameters and even not any RouteData available yet. That means we need to parse for the route values (here only the object's id) from the path. Parsing is a complex job especially when we need to make it fast. However I think using Regex for this purpose here is acceptable (although the framework code does not seem to like using Regex for the best performance). The framework code has a much more strict requirement for performance because it's the platform we build everything on. But in your code, you can take tradeoff between performance and easy-to-implement.
First we need a custom middleware like this:
public class EnsureExistenceByIdFromRouteValuesMiddleware
{
readonly RequestDelegate _next;
readonly EnsureExistenceByIdFromRouteValuesOptions _options;
public EnsureExistenceByIdFromRouteValuesMiddleware(RequestDelegate next,
EnsureExistenceByIdFromRouteValuesOptions options)
{
_next = next;
_options = options;
}
public async Task Invoke(HttpContext context)
{
var serviceType = _options.ExistenceCheckingServiceType;
var routePatterns = _options.RoutePatterns;
if (serviceType != null && routePatterns != null && routePatterns.Count > 0)
{
var service = context.RequestServices.GetRequiredService(_options.ExistenceCheckingServiceType) as IExistenceCheckingService;
if (service != null)
{
var matchedRoute = routePatterns.Select(e => Regex.Match(context.Request.Path,
e ?? "",
RegexOptions.Compiled | RegexOptions.IgnoreCase,
TimeSpan.FromSeconds(3)))
.FirstOrDefault(e => e.Success);
var id = matchedRoute?.Groups?.Skip(1)?.FirstOrDefault()?.Value;
if (!string.IsNullOrEmpty(id))
{
var isExisted = await service.ExistsAsync(id);
if (!isExisted && !context.Response.HasStarted)
{
context.Response.StatusCode = 404;
if (!_options.LetMvcHandle404)
{
return;
}
}
}
}
}
await _next(context);
}
}
The associated options class:
public class EnsureExistenceByIdFromRouteValuesOptions
{
public IList<string> RoutePatterns { get; } = new List<string>();
public Type ExistenceCheckingServiceType { get; set; }
public bool LetMvcHandle404 { get; set; }
public EnsureExistenceByIdFromRouteValuesOptions AddRoutePattern(string pattern)
{
RoutePatterns.Add(pattern);
return this;
}
public EnsureExistenceByIdFromRouteValuesOptions ClearRoutePatterns()
{
RoutePatterns.Clear();
return this;
}
}
Your services (for checking object existence) should implement a common & well-known interface (used by the middleware) like this:
public interface IExistenceCheckingService
{
Task<bool> ExistsAsync(object id);
}
//this is a sample implementation (just for demo)
public class ExistenceCheckingService : IExistenceCheckingService
{
public Task<bool> ExistsAsync(object id)
{
//dummy implementation for testing, only id of 1 is existed.
return Task.FromResult(Equals(id, "1"));
}
}
We create a convenient extension class for using in Startup.ConfigureServices and Startup.Configure:
public static class EnsureExistenceByIdFromRouteValuesExtensions
{
public static IServiceCollection EnsureExistenceByIdFromRouteValues(this IServiceCollection services)
{
//configure the MvcOptions to add the custom middleware
return services.Configure<MvcOptions>(o => {
o.Filters.Add(new EnsureExistenceByIdFromRouteValuesActionFilter());
}).AddScoped<IExistenceCheckingService, ExistenceCheckingService>();
}
public static IApplicationBuilder UseEnsureExistenceByIdFromRouteValuesMiddleware(this IApplicationBuilder app,
EnsureExistenceByIdFromRouteValuesOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
return app.UseMiddleware<EnsureExistenceByIdFromRouteValuesMiddleware>(options);
}
public static IApplicationBuilder UseEnsureExistenceByIdFromRouteValuesMiddleware(this IApplicationBuilder app,
Action<EnsureExistenceByIdFromRouteValuesOptions> configureOptions)
{
if (configureOptions == null) throw new ArgumentNullException(nameof(configureOptions));
var options = new EnsureExistenceByIdFromRouteValuesOptions();
configureOptions(options);
return app.UseEnsureExistenceByIdFromRouteValuesMiddleware(options);
}
//we use this filter for lately handling the 404 (set by the middleware)
class EnsureExistenceByIdFromRouteValuesActionFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context) {}
public void OnActionExecuting(ActionExecutingContext context)
{
if(context.HttpContext.Response.StatusCode == 404)
{
context.Result = new StatusCodeResult(404);
}
}
}
}
Use it in Startup.ConfigureServices:
services.EnsureExistenceByIdFromRouteValues();
Use it in Startup.Configure:
app.UseEnsureExistenceByIdFromRouteValuesMiddleware(o => {
//add your Regex patterns here
o.AddRoutePattern("/scorecard/(\\d+)/details/?$");
o.AddRoutePattern("/scorecard/(\\d+)/rankings/?$");
o.ExistenceCheckingServiceType = typeof(IExistenceCheckingService);
//setting this to true to not short-circuit right after the middleware
//the MVC middleware next will handle this (in the action filter)
//That way you will have a chance to use a custom view for 404
//(otherwise such as for Web APIs, we can let this be false as by default).
//o.LetMvcHandle404 = true;
});
NOTE: you need to know regex to use this. In the code above, I include just 2 sample regexes (matching your sample paths posted in your question). The regex patten must include one captured group for the object id (the (\\d+) in the sample patterns). That should be the first group (or should be the only group).
First you need to change the return type of the Get functions so that they can return a 404.
So:
public async Task<ScorecardDetails> Get(long scorecardId)
Becomes (pseudo-code):
public async Task<IActionResult> Get(long scorecardId) {
if(ScoreCardExists(scorecardId)) {
ScorecardDetails details = GetDetails(scorecardId);
return Ok(details);
}else{
return NotFound();
}
}

How to get instance of UserManager<IdentityUser> in Custom ActionFilterAttribute along with inputs [duplicate]

I am trying to inject a service into my action filter but I am not getting the required service injected in the constructor. Here is what I have:
public class EnsureUserLoggedIn : ActionFilterAttribute
{
private readonly ISessionService _sessionService;
public EnsureUserLoggedIn()
{
// I was unable able to remove the default ctor
// because of compilation error while using the
// attribute in my controller
}
public EnsureUserLoggedIn(ISessionService sessionService)
{
_sessionService = sessionService;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
// Problem: _sessionService is null here
if (_sessionService.LoggedInUser == null)
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Result = new JsonResult("Unauthorized");
}
}
}
And I am decorating my controller like so:
[Route("api/issues"), EnsureUserLoggedIn]
public class IssueController : Controller
{
}
Startup.cs
services.AddScoped<ISessionService, SessionService>();
Using these articles as reference:
ASP.NET Core Action Filters
Action filters, service filters and type filters in ASP.NET 5 and MVC 6
Using the filter as a ServiceFilter
Because the filter will be used as a ServiceType, it needs to be registered with the framework IoC. If the action filters were used directly, this would not be required.
Startup.cs
public void ConfigureServices(IServiceCollection services) {
services.AddMvc();
services.AddScoped<ISessionService, SessionService>();
services.AddScoped<EnsureUserLoggedIn>();
...
}
Custom filters are added to the MVC controller method and the controller class using the ServiceFilter attribute like so:
[ServiceFilter(typeof(EnsureUserLoggedIn))]
[Route("api/issues")]
public class IssueController : Controller {
// GET: api/issues
[HttpGet]
[ServiceFilter(typeof(EnsureUserLoggedIn))]
public IEnumerable<string> Get(){...}
}
There were other examples of
Using the filter as a global filter
Using the filter with base controllers
Using the filter with an order
Take a look, give them a try and see if that resolves your issue.
Hope this helps.
Global filters
You need to implement IFilterFactory:
public class AuthorizationFilterFactory : IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
// manually find and inject necessary dependencies.
var context = (IMyContext)serviceProvider.GetService(typeof(IMyContext));
return new AuthorizationFilter(context);
}
}
In Startup class instead of registering an actual filter you register your filter factory:
services.AddMvc(options =>
{
options.Filters.Add(new AuthorizationFilterFactory());
});
One more way for resolving this problem. You can get your service via Context as in the following code:
public override void OnActionExecuting(ActionExecutingContext context)
{
_sessionService = context.HttpContext.RequestServices.GetService<ISessionService>();
if (_sessionService.LoggedInUser == null)
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Result = new JsonResult("Unauthorized");
}
}
Please note that you have to register this service in Startup.cs
services.AddTransient<ISessionService, SessionService>();
Example
private ILoginService _loginService;
public override void OnActionExecuting(ActionExecutingContext context)
{
_loginService = (ILoginService)context.HttpContext.RequestServices.GetService(typeof(ILoginService));
}
Hope it helps.
After reading this article ASP.NET Core - Real-World ASP.NET Core MVC Filters (Aug 2016) I implemented it like this:
In Starup.cs / ConfigureServices:
services.AddScoped<MyService>();
In MyFilterAttribute.cs:
public class MyFilterAttribute : TypeFilterAttribute
{
public MyFilterAttribute() : base(typeof (MyFilterAttributeImpl))
{
}
private class MyFilterAttributeImpl : IActionFilter
{
private readonly MyService _sv;
public MyFilterAttributeImpl(MyService sv)
{
_sv = sv;
}
public void OnActionExecuting(ActionExecutingContext context)
{
_sv.MyServiceMethod1();
}
public void OnActionExecuted(ActionExecutedContext context)
{
_sv.MyServiceMethod2();
}
}
}
In MyFooController.cs :
[MyFilter]
public IActionResult MyAction()
{
}
Edit: Passing arguments like [MyFilter("Something")] can be done using the Arguments property of the TypeFilterAttribute class: How do I add a parameter to an action filter in asp.net? (rboe's code also shows how to inject things (the same way))
While the question implicitly refers to "filters via attributes", it is still worth highlighting that adding filters "globally by type" supports DI out-of-the-box:
[For global filters added by type] any constructor dependencies will be populated by dependency injection (DI). Adding a filter by type is equivalent to filters.Add(new TypeFilterAttribute(typeof(MyFilter))).
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection
With regards to attribute-based filters:
Filters that are implemented as attributes and added directly to controller classes or action methods cannot have constructor dependencies provided by dependency injection (DI). This is because attributes must have their constructor parameters supplied where they're applied. This is a limitation of how attributes work.
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection
However, as mentioned in the previous answers to the OP, there are ways of indirection that can be used to achieve DI. For the sake of completeness, here are the links to the official docs:
ServiceFilterAttribute
TypeFilterAttribute
IFilterFactory implemented on your attribute

Asp.Net Core: Access custom AuthorizeAttribute property in AuthorizeHandler

As I am working on Asp.Net core Authorization part, I needed a new property in AuthorizeAttribute which I want to utilize as a extra permission value. So, I have extended the AuthorizeAttribute in my own custom Authorize attribute. See below:
public class RoleAuthorizeAttribute : Microsoft.AspNetCore.Authorization.AuthorizeAttribute
{
public string Permission { get; private set; }
public RoleAuthorizeAttribute(string policy, string permission) : base(policy)
{
this.Permission = permission;
}
}
Then, I've created an AuthorizationHandler to check for the requirement as below:
public class RolePermissionAccessRequirement : AuthorizationHandler<RolePermissionDb>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RolePermissionDb requirement)
{
// check here..
context.Succeed(requirement);
return Task.FromResult(0);
}
}
All respective service collection mapping I have already done, just omitted here.
Now, I want my attribute to use like this on controller action level:
[RoleAuthorize("DefaultPolicy", "CustomPermission")]
public IActionResult List()
{
}
Would anybody suggest me how would I access the permission property value given on the top of Action method in the handler RolePermissionAccessRequirement ??
I want to perform some sort of access rule based on custom permission value given in the Authorize attribute on top of Action method.
Thanks in advance!
To parametrize a custom Authorize attribute, create an authorization filter implementing IAsyncAuthorizationFilter. Then wrap the filter in a TypeFilterAttribute-derived attribute. This attribute can accept parameters and pass it to the authorization filter's constructor.
Usage example:
[AuthorizePermission(Permission.Foo, Permission.Bar)]
public IActionResult Index()
{
return View();
}
Implementation:
public class AuthorizePermissionAttribute : TypeFilterAttribute
{
public AuthorizePermissionAttribute(params Permission[] permissions)
: base(typeof(PermissionFilter))
{
Arguments = new[] { new PermissionRequirement(permissions) };
Order = Int32.MinValue;
}
}
public class PermissionFilter : Attribute, IAsyncAuthorizationFilter
{
private readonly IAuthorizationService _authService;
private readonly PermissionRequirement _requirement;
public PermissionFilter(
IAuthorizationService authService,
PermissionRequirement requirement)
{
//you can inject dependencies via DI
_authService = authService;
//the requirement contains permissions you set in attribute above
//for example: Permission.Foo, Permission.Bar
_requirement = requirement;
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
bool ok = await _authService.AuthorizeAsync(
context.HttpContext.User, null, _requirement);
if (!ok) context.Result = new ChallengeResult();
}
}
In addition, register a PermissionHandler in DI to handle PermissionRequirement with permission list:
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
Look at this this GitHub project for a complete example.

How to mock owin's authorizationManager to test my api controller

I'm using ThinkTecture's resource based authorization in my WebApi.
I'm trying to test one of my controller that I needed to check the access inside the function. But now, I can't test the function anymore since, I can't mock an extension method and since it's a nuget method, I can't modify the class to inject another value.
My controller look like this:
public class AlbumController : ApiController
{
public async Task<IHttpActionResult> Get(int id)
{
if (!(await Request.CheckAccessAsync(ChinookResources.AlbumActions.View,
ChinookResources.Album,
id.ToString())))
{
return this.AccessDenied();
}
return Ok();
}
}
And the ResourceAuthorizationManager is setted into the startup like this:
app.UseResourceAuthorization(new ChinookAuthorization());
Source code of the ThinkTecture project is here.
Thank you for your help
The ResourceAuthorizationAttribute uses Reqest.CheckAccess so I don't think it is a good solution to abstract away the implementation and then injecting it into the controller since in theory, the ResourceAuthorizationAttribute and the created service could use different implementations of the CheckAccess method.
I took a simpler approach by creating a BaseController
public class BaseController : ApiController
{
public virtual Task<bool> CheckAccessAsync(string action, params string[] resources)
{
return Request.CheckAccessAsync(action, resources);
}
}
and making CheckAccessAsync virtual so I can mock it (by for example Moq).
then from my controller
public class AlbumController : BaseController
{
public async Task<IHttpActionResult> Get(int id)
{
if (!(await CheckAccessAsync(ChinookResources.AlbumActions.View,
ChinookResources.Album,
id.ToString())))
{
return this.AccessDenied();
}
return Ok();
}
}
Unit testing the controller then is as easy as:
[TestClass]
public class TestClass
{
Mock<AlbumController> mockedTarget
AlbumController target
[TestInitialize]
public void Init()
{
mockedTarget = new Mock<AlbumController>();
target = mockedTarget.Object;
}
[Test]
public void Test()
{
mockedTarget.Setup(x => x.CheckAccessAsync(It.IsAny<string>(),
It.IsAny<string[]>()))
.Returns(Task.FromResult(true));
var result = target.Get(1);
// Assert
}
}
You could always wrap this static call into some abstraction of yours:
public interface IAuthorizationService
{
Task<bool> CheckAccessAsync(string view, string album, string id);
}
and then have some implementation that will delegate the call to the static extension method. But now since you will be working with the IAuthorizationService you can freely mock the CheckAccessAsync method in your unit tests.
As far as testing the implementation of this abstraction is concerned, you probably don't need it as it only acts as a bridge to the ThinkTecture's classes which should already be pretty well tested.
I finally solved my problem.
The real problem was that the CheckAccess method was an extension.
(for my answer, every class will refer to the sample that can be find here)
To stop using the extension method, I added these methods into my chinookAuthorization
public Task<bool> CheckAccessAsync(ClaimsPrincipal user, string action, params string[] resources)
{
var ctx = new ResourceAuthorizationContext(user ?? Principal.Anonymous, action, resources);
return CheckAccessAsync(ctx);
}
public Task<bool> CheckAccessAsync(ClaimsPrincipal user, IEnumerable<Claim> actions, IEnumerable<Claim> resources)
{
var authorizationContext = new ResourceAuthorizationContext(
user ?? Principal.Anonymous,
actions,
resources);
return CheckAccessAsync(authorizationContext);
}
Then I changed my controller to have an instance of the chinookAuthorization
public class AlbumController : ApiController
{
protected readonly chinookAuthorization chinookAuth;
public BaseApiController(chinookAuthorization chinookAuth)
{
if (chinookAuth == null)
throw new ArgumentNullException("chinookAuth");
this.chinookAuth = chinookAuth;
}
public async Task<IHttpActionResult> Get(int id)
{
if (!(await chinookAuth.CheckAccessAsync((ClaimsPrincipal)RequestContext.Principal, ChinookResources.AlbumActions.View,
ChinookResources.Album,
id.ToString())))
{
return this.AccessDenied();
}
return Ok();
}
}
And I'm still declaring my ChinookAuthorization into my owin startup, to keep using the same pattern for my attribute check access call.
So now, I just have to mock the chinookAuthorization, mock the response of the call to return true, and that's it!

Categories