DI into a Requirement/Policy in ASP.NET MVC 6 - c#

I'm looking for a way to program a custom authorization filter in ASP.NET 5 as the current implementation relies in Policies/Requirements which in turn rely solely in the use of Claims, thus on the umpteenth and ever-changing Identity System of which I'm really tired of (I've tried all it's flavors).
I have a large set of permissions (over 200) which I don't want to code as Claims as I have my own repository for them and a lot faster way to be check against it than comparing hundreds of strings (that is what claims are in the end).
I need to pass a parameter in each attribute that should be checked against my custom repository of permissions:
[Authorize(Requires = enumPermission.DeleteCustomer)]
I know this is not the most frequent scenario, but I think it isn't an edge case. I've tried implementing it in the way described by #leastprivilege on his magnificent post "The State of Security in ASP.NET 5 and MVC 6: Authorization", but I've hit the same walls as the author, who has even opened an issue on the ASP.NET 5 github repo, which has been closed in a not too much clarifying manner: link
Any idea of how to achieve this? Maybe using other kind of filter? In that case, how?

Following is an example of how you can achieve this scenario:
Let's assume you have a service called IPermissionStore which validates if a given user has the required permissions specified on the attribute.
public class MyCustomAuthorizationFilterAttribute : Attribute, IFilterFactory, IOrderedFilter
{
private readonly Permision[] _permissions;
public MyCustomAuthorizationFilterAttribute(params Permision[] permissions)
{
_permissions = permissions;
}
public int Order { get; set; }
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
var store = serviceProvider.GetRequiredService<IPermissionStore>();
return new MyCustomAuthorizationFilter(store, _permissions)
{
Order = Order
};
}
}
public class MyCustomAuthorizationFilter : IAuthorizationFilter, IOrderedFilter
{
private readonly IPermissionStore _store;
private readonly Permision[] _permissions;
public int Order { get; set; }
public MyCustomAuthorizationFilter(IPermissionStore store, params Permision[] permissions)
{
_store = store;
_permissions = permissions;
}
public void OnAuthorization(AuthorizationContext context)
{
// Check if the action has an AllowAnonymous filter
if (!HasAllowAnonymous(context))
{
var user = context.HttpContext.User;
var userIsAnonymous =
user == null ||
user.Identity == null ||
!user.Identity.IsAuthenticated;
if (userIsAnonymous)
{
Fail(context);
}
else
{
// check the store for permissions for the current user
}
}
}
private bool HasAllowAnonymous(AuthorizationContext context)
{
return context.Filters.Any(item => item is Microsoft.AspNet.Authorization.IAllowAnonymous);
}
private void Fail(AuthorizationContext context)
{
context.Result = new HttpUnauthorizedResult();
}
}
// Your action
[HttpGet]
[MyCustomAuthorizationFilter(Permision.CreateCustomer)]
public IEnumerable<string> Get()
{
//blah
}

Related

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();
}
}

Custom Attribute settings in .net Core

I am writing a very simple custom attribute to be used with my methods for ASP.net Core. The attribute is to handle feature flags which indicate an endpoint method is "switched on or off" as follows:
1) If a feature is turned ON, allow the code to pass through to the method and execute it as normal.
2) If the feature is turned OFF, just return from the attribute and dont execute the method within
I was thinking something along the lines of this:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class FeatureFlagAttribute : ActionFilterAttribute
{
private Dictionary<string, bool> myFeaturesList;
private readonly string selectedFeature;
public FeatureFlagAttribute(string featureName)
{
selectedFeature = featureName;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var found = myFeaturesList.TryGetValue(selectedFeature, out var result);
if (!found || !result)
{
// dont continue
context.HttpContext.Response.StatusCode = 403;
}
}
}
I need the myFeaturesList populated for this to work BUT I dont want to pass it into the constructor every time this is being used. Whats the best way to configure this? I was thinking of setting a static property in the attribute but thought this was a bit of a lame approach and that there must be a better way. Thanks in advance!
Alternatively, you could extract the creation of featureNames into an injectable service (registered to DI) and use your attribute as a type filter or with IFilterFactory.
Using type filters, you would create your attribute as:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class FeatureFlagAttribute : ActionFilterAttribute
{
private readonly string _featureName;
private readonly IFeatureService _featureService;
public FeatureFlagAttribute(string featureName, IFeatureService featureService)
{
_featureName = featureName;
_featureService = featureService;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var features = _featureService.GetMyFeatures();
var found = features.TryGetValue(_featureName, out var result);
if (!found || !result)
{
// don't continue
context.HttpContext.Response.StatusCode = 403;
}
}
}
In the constructor parameters, featureName stays the same, and needs to be defined to the attribute, while featureService will get resolved from the DI, so you need to register an implementation for this in your startup's ConfigureServices().
The attribute usage changes a bit with type filters, for example:
[TypeFilter(typeof(FeatureFlagAttribute), Arguments = new object[] { "feature-A" })]
public IActionResult Index()
{
return View();
}
You can read more options of injecting dependencies into filters in the docs:
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection
A different approach, but maybe move that out of the attribute, perhaps using a static event as the API hook? then you can put the dictionary where-ever you want?
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class FeatureFlagAttribute : ActionFilterAttribute
{
public FeatureFlagAttribute(string featureName)
{
selectedFeature = featureName;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (IsActive?.Invoke(selectedFeature) == false)
{
// dont continue
context.HttpContext.Response.StatusCode = 403;
}
}
public static event Func<string, bool> IsActive;
}
(note that you need to be careful with static events not to cause memory leaks)
Alternatively, keep what you have, but make the dictionary static (and thread-protected, etc); then add some kind of API like:
public static void SetFeatureEnabled(string featureName, bool enabled);
that tweaks the static dictionary.

how to implement this mvc filter attribute?

I need to integrate a new Security API into several existing applications in my organization. Some applications use ASP.NET MVC and use the .NET AuthorizeAttribute class to decorate classes with security.
For example:
[Authorize(Roles="MY_CORP\Group1,MY_CORP\Group2")]
public class MyClass
{
//
}
The code above is based on a Windows authentication configuration. I need to update this implementation to use the new Security API. The new Security API will retrieve a user like this:
var user = new SecurityApi().GetUser(userId);
var groups = user.Groups;
So ideally the updated decorator would look something like this, where GroupX and GroupY exist as user.Groups returned from the Security API:
[Authorize(Roles="GroupX, GroupY")]
public class MyClass
{
//
}
Any idea how I would go about implementing this?
I use something along the lines of this:
public class RequireAuthAttribute : TypeFilterAttribute
{
public RequireAuthAttribute(params Roles[] rolesRequirement)
: base(typeof(RequireAuthFilter))
{
Arguments = new object[] { rolesRequirement };
}
public enum Roles: ushort
{
CompanyOnly,
AuthenticatedCustomer,
AuthorizedCustomer,
AuthorizedOwnerManager
}
}
With:
public class RequireAuthFilter : IAsyncActionFilter
{
private readonly Roles[] _rolesToAllow;
public RequireAuthFilter(Roles[] rolesRequirement = default(Roles[]))
{
_rolesToAllow = rolesRequirement;
}
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next )
{
// Verify is Authenticated
if (context.HttpContext.User.Identity.IsAuthenticated != true)
{
context.HttpContext.SetResponse(401, "User is not Authenticated");
return;
}
var isCompanyAdmin = context.HttpContext.IsCompanyAdmin();
// ^ HttpContext Extension method that looks at our JWT Token
// and determines if has required Cliams/Roles.
if (isCompanyAdmin == true)
{
await next();
return;
} else {
context.HttpContext.SetResponse(401, "Restricted to Company");
return;
}
// Other custom logic for each role.
// You will want to decide if comma represents AND or an OR
// when specifying roles.
}
}
And use like this:
[RequireAuth(Roles.CompanyOnly, Roles.AuthorizedOwnerManager)]
public class MyClass
{
//
}

Custom Bearer Token Authorization for ASP.Net Core

Is this an acceptable implementation of a custom bearer token authorization mechanism?
Authorization Attribute
public class AuthorizeAttribute : TypeFilterAttribute
{
public AuthorizeAttribute(): base(typeof(AuthorizeActionFilter)){}
}
public class AuthorizeActionFilter : IAsyncActionFilter
{
private readonly IValidateBearerToken _authToken;
public AuthorizeActionFilter(IValidateBearerToken authToken)
{
_authToken = authToken;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
const string AUTHKEY = "authorization";
var headers = context.HttpContext.Request.Headers;
if (headers.ContainsKey(AUTHKEY))
{
bool isAuthorized = _authToken.Validate(headers[AUTHKEY]);
if (!isAuthorized)
context.Result = new UnauthorizedResult();
else
await next();
}
else
context.Result = new UnauthorizedResult();
}
}
Validation Service. APISettings class is used in appSettings, but validation can be extended to use a database ... obviously :)
public class APISettings
{
public string Key { get; set; }
}
public class ValidateBearerToken : IValidateBearerToken
{
private readonly APISettings _bearer;
public ValidateBearerToken(IOptions<APISettings> bearer)
{
_bearer = bearer.Value;
}
public bool Validate(string bearer)
{
return (bearer.Equals($"Bearer {_bearer.Key}"));
}
}
Implementation
[Produces("application/json")]
[Route("api/my")]
[Authorize]
public class MyController : Controller
appSettings
"APISettings": {
"Key": "372F78BC6B66F3CEAF705FE57A91F369A5BE956692A4DA7DE16CAD71113CF046"
}
Request Header
Authorization: Bearer 372F78BC6B66F3CEAF705FE57A91F369A5BE956692A4DA7DE16CAD71113CF046
That would work, but it's kind of reinventing the wheel.
I good approach these days is to use JWTs, you can find more info about it here: http://www.jwt.io/
Some advantages are that it integrates quite nicely with asp.net core and you can also add some information to the token (username, role, etc). That way, you don't even need to access the database for validation (if you want to).
Also, storing keys in appsettings file could lead to accidentally adding them to your source-code manager (security). You could use user secrets for local development (or disable the key when environment = dev) and environment variables for production.
Here is one good example of how to use jwt with asp.net: https://jonhilton.net/2017/10/11/secure-your-asp.net-core-2.0-api-part-1-issuing-a-jwt/

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.

Categories