I have defined 2 policies, ADD and SUB as shown below.
options.AddPolicy("ADD", policy =>
policy.RequireClaim("Addition", "add"));
options.AddPolicy("SUB", policy =>
policy.RequireClaim("Substraction", "subs"));
All what I want to do isto include 2 policies on a controller method. How can I perform this operation.
[Authorize(Policy = "ADD, SUB")]
[HttpPost]
public IActionResult PerformCalculation()
{
}
However, this gives me an error:
InvalidOperationException: The AuthorizationPolicy named: 'ADD, SUB' was not found
The first thing to realize is that Authorize attribute Policy setting is singular unlike Roles which can be plural and that multiple policies are treated on an AND basis, unlike a list of roles which is treated on an OR basis.
In your example code “ADD, SUB” is considered a single policy name. If you want to attribute you method with both policies, your code should be as follows.
[Authorize(Policy = "ADD")]
[Authorize(Policy = "SUB")]
[HttpPost]
public IActionResult PerformCalculation()
{
}
However, this will not give you the effect you want of either or, since policies are AND together, hence both policies must pass to be authorized. Nor will the suggestions of writing a single policy or a requirements handler to handle the multiple requirements give you the result of treating policies on a OR basis.
Instead, the solution is to create a TypeFilterAttribute that accepts a list of policies and is tied to a IAsyncAuthorizationFilter that test for either or. The following outlines the two classes you will need to define and how to attribute your action method.
The following code defines the new attribute AuthorizeAnyPolicy class.
/// <summary>
/// Specifies that the class or method that this attribute is applied to requires
/// authorization based on user passing any one policy in the provided list of policies.
/// </summary>
public class AuthorizeAnyPolicyAttribute : TypeFilterAttribute
{
/// <summary>
/// Initializes a new instance of the AuthorizeAnyPolicyAttribute class.
/// </summary>
/// <param name="policies">A comma delimited list of policies that are allowed to access the resource.</param>
public AuthorizeAnyPolicyAttribute(string policies) : base(typeof(AuthorizeAnyPolicyFilter))
{
Arguments = new object[] { policies };
}
}
The following code defines the authorization filter class which loops through and executes each policy in the list. Should all the policies fail the result of the authorization context is set to forbid.
public class AuthorizeAnyPolicyFilter : IAsyncAuthorizationFilter
{
private readonly IAuthorizationService authorization;
public string Policies { get; private set; }
/// <summary>
/// Initializes a new instance of the AuthorizeAnyPolicyFilter class.
/// </summary>
/// <param name="policies">A comma delimited list of policies that are allowed to access the resource.</param>
/// <param name="authorization">The AuthorizationFilterContext.</param>
public AuthorizeAnyPolicyFilter(string policies, IAuthorizationService authorization)
{
Policies = policies;
this.authorization = authorization;
}
/// <summary>
/// Called early in the filter pipeline to confirm request is authorized.
/// </summary>
/// <param name="context">A context for authorization filters i.e. IAuthorizationFilter and IAsyncAuthorizationFilter implementations.</param>
/// <returns>Sets the context.Result to ForbidResult() if the user fails all of the policies listed.</returns>
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
var policies = Policies.Split(",").ToList();
// Loop through policies. User need only belong to one policy to be authorized.
foreach (var policy in policies)
{
var authorized = await authorization.AuthorizeAsync(context.HttpContext.User, policy);
if (authorized.Succeeded)
{
return;
}
}
context.Result = new ForbidResult();
return;
}
}
With the policies defined as shown in the question you would attribute the method as follows.
[AuthorizeAnyPolicy("ADD,SUB")]
[HttpPost]
public IActionResult PerformCalculation()
{
}
It’s that simple and you will find similar solutions in the following Stack Overflow questions.
Authorize against a list of policies
How to add multiple policies in action using Authorize attribute using identity 2.0?
Related
I have a class as shown below,
public class AccessToken : IAuthToken
{
/// <summary>
/// Initializes a new instance of the <see cref="AccessToken"/> class.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="validTo">The valid to.</param>
public AccessToken(string token, DateTimeOffset validTo)
{
ValidTo = validTo;
Token = token;
}
///<inheritdoc />
public DateTimeOffset ValidTo { get; private set; }
/// <summary>
/// Gets the RAW Jwt token value.
/// This value is encoded and represents the Jwt token.
/// </summary>
/// <value>
/// The token.
/// </value>
public string Token { get; private set; }
}
The DI code goes like this,
return services
.AddTransient<IAuthToken, AccessToken>()
.AddTransient<IAuthTokenService, AuthTokenService>()
.AddSingleton<IIdentityDiscovery, IdentityDiscovery>()
.AddTransient<IIdentityTokenClient, IdentityTokenClient>()
.AddTransient<IDiscoveryClientRetryPolicy, DiscoveryClientRetryPolicy>()
.AddTransient<ITokenClientRetryPolicy, TokenClientRetryPolicy>()
.AddSingleton<IRetryPolicyOptions>(provider => retryOptions);
The whole thing is packaged as a nuget. When this DI code is called from .net461 it works fine but when used in net core 6, I get an error stating it's unable to resolve type string and DateTimeOffset. I tried to inject a dummy string and the string error vanished but the struct DateTimeOffset persisted. Does .net6 interpret this DI in a different way?
If your classes in your nuget package tries to resolve this IAuthToken instance then you can add the following registration to your main app to override the registration that comes from the nuget. The registration order is important, so be sure to execute service registration from your nuget first.
services.AddTransient<IAuthToken, AccessToken>(
serviceProvider =>
{
// You can resolve any service that can provide the token and validTo parameters.
var tokenInfoProvider = serviceProvider.GetRequiredService<...>();
// Get the parameter values.
// Either from the tokenInfoProvider or specify it directly here.
string token = String.Empty;
DateTimeOffset validTo = DateTimeOffset.UtcNow.AddHours(1);
// Create and return the access token instance.
return new AccessToken(token, validTo);
}
);
If only the app tries to resolve an IAuthToken then I would suggest to create a factory (e.g. IAccessTokenFactory) that you can use to create the AccessToken instance with specific ctor parameters dynamically. Then register its implementation and inject IAccessTokenFactory where you need to get a new IAuthToken instance. So that you can keep your nuget code as it is now.
public interface IAccessTokenFactory
{
IAuthToken Create(string token, DateTimeOffset validTo);
}
public class AccessTokenFactory : IAccessTokenFactory
{
public IAuthToken Create(string token, DateTimeOffset validTo)
{
return new AccessToken(token, validTo);
}
}
// register this instead of the AccessToken
services.AddSingleton<IAccessTokenFactory, AccessTokenFactory>();
I have been reasing about owned instances and I have a need to set one up.
http://docs.autofac.org/en/latest/advanced/owned-instances.html
I have 2 methods in the same class that use this owned instance. I have set it up like this:
private readonly Func<SessionDetails, Owned<ITroposUnitOfWork>> _paramatizedTroposUnitOfWork;
My unit of work class constructor looks like this:
/// <summary>
/// Used for creating manual sessions
/// </summary>
/// <param name="sessionDetails">The details of the session to be created</param>
public TroposUnitOfWork(SessionDetails sessionDetails)
{
// Throw if we don't supply any details
ThrowIf.ArgumentIsNull(() => sessionDetails);
// Start the session
StartSession(sessionDetails);
}
So, my understanding is that if I use a using block, then the unit of work will be disposed of at the end of the call. But it isn't.
Like I mentioned before, I have 2 methods that use this owned instance. They are:
/// <summary>
/// Creates the Tropos user
/// </summary>
/// <param name="model">The user to be created</param>
/// <param name="password">The password to set</param>
private async Task CreateTroposUserAsync(User model, string password)
{
// If there is no password, throw an error
ThrowIf.ArgumentIsNull(() => password);
// Get our master user
var user = await base.FindByNameAsync(model.Master);
// If we have no master user, throw an error
if (user == null) throw new ObjectNotFoundException();
// Create our session details
var sessionDetails = _troposSession.Value.Create(user);
// User our new user
using (var troposUnitOfWork = _paramatizedTroposUnitOfWork(sessionDetails))
{
try
{
// Create our tropos user service
var userService = new TroposUserService(troposUnitOfWork.Value);
// Create our user
var transaction = userService.Create(model);
// Save our changes (Don't throw an error if the user already exists)
troposUnitOfWork.Value.RunTransaction(transaction);
} catch (Exception ex)
{
// Display human readable messages
throw new Exception(ex.Message);
}
}
// Sets the new users password
SetTroposPassword(model, password);
// Update the flag
model.HasTroposLogin = true;
}
And the other one is:
/// <summary>
/// Sets the tropos password
/// </summary>
/// <param name="model">The user that needs the password setting</param>
/// <param name="password"></param>
private void SetTroposPassword(User model, string password)
{
// Create our session details
var sessionDetails = _troposSession.Value.Create(model.UserName);
// Create our anonymous session
using (var troposUnitOfWork = _paramatizedTroposUnitOfWork(sessionDetails))
{
// Create our tropos user service
var userService = new TroposUserService(troposUnitOfWork.Value);
// Set our password
var transaction = userService.ChangePassword(password);
// Save our changes
troposUnitOfWork.Value.RunTransaction(transaction);
}
}
The first method does invoke the second method, but outside the using block. I put a breakpoint in the TroposUnitOfWork dispose method and it only gets hit once. The constructor is only hit once too.
Does anyone know why?
We need to see the initialization of _paramatizedTroposUnitOfWork.
What class has the CreateTroposUserAsync method? We need to see the constructor of that class. I suppose your overall goal is to get a unit of work implementation.
The reason why you only hit constructor once, might be due to the lifetime you used when registering. If its Owned. Then the two methods probably execute in the same lifetime scope and the dependency is only resolved once. Or put differently the _paramatizedTroposUnitOfWork(sessionDetails) returns the same instance.
I've solved similar problem using decorators and factories
public interface IEventHandlerFactory<in TNotification> where TNotification
: class, IAsyncNotification
{
IAsyncNotificationHandler<TNotification> Create( ILifetimeScope
lifetimeScope );
}
public class EventHandlerFactory<TNotification, TEventHandler> :
IEventHandlerFactory<TNotification>
where TNotification : class, IAsyncNotification
where TEventHandler : class, IAsyncNotificationHandler<TNotification>
{
public IAsyncNotificationHandler<TNotification> Create( ILifetimeScope
lifetimeScope )
{
return lifetimeScope.ResolveNamed<TEventHandler>( "EventHandler" )
as IAsyncNotificationHandler<TNotification>;
}
}
full .net fiddle is here https://dotnetfiddle.net/fw4IBw
I'm having trouble specifying two separate Authorization attributes on a class method: the user is to be allowed access if either of the two attributes are true.
The Athorization class looks like this:
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class AuthAttribute : AuthorizeAttribute {
. . .
and the action:
[Auth(Roles = AuthRole.SuperAdministrator)]
[Auth(Roles = AuthRole.Administrator, Module = ModuleID.SomeModule)]
public ActionResult Index() {
return View(GetIndexViewModel());
}
Is there a way to solve this or do I need to rethink my approach?
This is to be run in MVC2.
There is a better way to do this in later versions of asp.net you can do both OR and AND on roles. This is done through convention, listing multiple roles in a single Authorize will perform an OR where adding Multiple Authorize Attributes will perform AND.
OR example
[Authorize(Roles = "PowerUser,ControlPanelUser")]
AND Example
[Authorize(Roles = "PowerUser")]
[Authorize(Roles = "ControlPanelUser")]
You can find more info on this at the following link
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/roles
Multiple AuthorizeAttribute instances are processed by MVC as if they were joined with AND. If you want an OR behaviour you will need to implement your own logic for checks. Preferably implement AuthAttribute to take multiple roles and perform an own check with OR logic.
Another solution is to use standard AuthorizeAttribute and implement custom IPrincipal that will implement bool IsInRole(string role) method to provide 'OR' behaviour.
An example is here:
https://stackoverflow.com/a/10754108/449906
I've been using this solution in production environment for awhile now, using .NET Core 3.0. I wanted the OR behavior between a custom attribute and the native AuthorizeAttribute. To do so, I implemented the IAuthorizationEvaluator interface, which gets called as soon as all authorizers evaluate theirs results.
/// <summary>
/// Responsible for evaluating if authorization was successful or not, after execution of
/// authorization handler pipelines.
/// This class was implemented because MVC default behavior is to apply an AND behavior
/// with the result of each authorization handler. But to allow our API to have multiple
/// authorization handlers, in which the final authorization result is if ANY handlers return
/// true, the class <cref name="IAuthorizationEvaluator" /> had to be extended to add this
/// OR behavior.
/// </summary>
public class CustomAuthorizationEvaluator : IAuthorizationEvaluator
{
/// <summary>
/// Evaluates the results of all authorization handlers called in the pipeline.
/// Will fail if: at least ONE authorization handler calls context.Fail() OR none of
/// authorization handlers call context.Success().
/// Will succeed if: at least one authorization handler calls context.Success().
/// </summary>
/// <param name="context">Shared context among handlers.</param>
/// <returns>Authorization result.</returns>
public AuthorizationResult Evaluate(AuthorizationHandlerContext context)
{
// If context.Fail() got called in ANY of the authorization handlers:
if (context.HasFailed == true)
{
return AuthorizationResult.Failed(AuthorizationFailure.ExplicitFail());
}
// If none handler called context.Fail(), some of them could have called
// context.Success(). MVC treats the context.HasSucceeded with an AND behavior,
// meaning that if one of the custom authorization handlers have called
// context.Success() and others didn't, the property context.HasSucceeded will be
// false. Thus, this class is responsible for applying the OR behavior instead of
// the default AND.
bool success =
context.PendingRequirements.Count() < context.Requirements.Count();
return success == true
? AuthorizationResult.Success()
: AuthorizationResult.Failed(AuthorizationFailure.ExplicitFail());
}
}
This evaluator will only be called if added to .NET service collection (in your startup class) as follows:
services.AddSingleton<IAuthorizationEvaluator, CustomAuthorizationEvaluator>();
In the controller class, decorate each method with both attributes. In my case [Authorize] and [CustomAuthorize].
I'm not sure how others feel about this but I wanted an OR behavior too. In my AuthorizationHandlers I just called Succeed if any of them passed. Note this did NOT work with the built-in Authorize attribute that has no parameters.
public class LoggedInHandler : AuthorizationHandler<LoggedInAuthReq>
{
private readonly IHttpContextAccessor httpContextAccessor;
public LoggedInHandler(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, LoggedInAuthReq requirement)
{
var httpContext = httpContextAccessor.HttpContext;
if (httpContext != null && requirement.IsLoggedIn())
{
context.Succeed(requirement);
foreach (var req in context.Requirements)
{
context.Succeed(req);
}
}
return Task.CompletedTask;
}
}
Supply your own LoggedInAuthReq. In startup inject these in services with
services.AddAuthorization(o => {
o.AddPolicy("AadLoggedIn", policy => policy.AddRequirements(new LoggedInAuthReq()));
... more here
});
services.AddSingleton<IAuthorizationHandler, LoggedInHandler>();
... more here
And in your controller method
[Authorize("FacebookLoggedIn")]
[Authorize("MsaLoggedIn")]
[Authorize("AadLoggedIn")]
[HttpGet("anyuser")]
public JsonResult AnyUser()
{
return new JsonResult(new { I = "did it with Any User!" })
{
StatusCode = (int)HttpStatusCode.OK,
};
}
This could probably also be accomplished with a single attribute and a bunch of if statements. It works for me in this scenario. asp.net core 2.2 as of this writing.
First of all I wanted to thank all of you for your continuous contributions to the Stack Overflow community! I've been a member of Stack Overflow for years and have come to rely on your input more so than any other source online. Though I try to participate and answer members' questions whenever I can, every once in a while I find myself stuck and in need of help.
Speaking of which I have an unusual code problem. I am writing an API library in C# that needs to be able to be called from WPF/Windows Forms application, but also from within Unit Test code.
The issue is that I need to be able to report (in Excel) on whether each method of the library executed properly when the API is called from within a WPF/windows forms application, along some other metadata and optionally a return type.
When the code is consumed within Unit Tests I don't really care about the reporting, but I do need to be able to produce an Assert on whether the API call executed properly or not.
For instance, if in a Unit Test we have an Test Initialize portion, one of the API calls may be to create a Domain User for the test method to use. Another one may also create a Domain Group, so that the user has proper group membership.
To accomodate the consumption of the API from WPF/WinForms, I've been rewriting every function in the API to return a OperationStep type, with the hopes that when all API calls have executed I would have an IEnumerable<OperationStep> which I can write to a CSV file.
So the question is is there an easier way of achieving what I have done so far? The reporting is extremely tedious and time consuming to code, considering that the API library consists of hundreds of similar methods. Samples are described bellow:
OperationStep<PrincipalContext> createDomainConnectionStep = DomainContext.Current.GetPrincipalContext(settings.DomainInfo);
OperationStep<UserPrincipal> createDomainUserStep = DomainContext.Current.CreateUser(createDomainConnectionStep.Context, settings.TestAccountInfo.Username, settings.TestAccountInfo.Password);
OperationStep<GroupPrincipal> createDomainGroupStep = DomainContext.Current.CreateGroup(createDomainConnectionStep.Context, settings.TestAccountInfo.UserGrupName);
Where the DomainContext is a singleton object whose functionality is to connect to the domain controller and create a user, group, and associate the user to a group.
Note that both the second and the third method call require the output of the first, and therefore warranting the need for having the public T Context within the OperationResult object as described bellow.
The OperationStep object consists of the following properties which are inherited by the IOperation interface with the exception of the public T Context.
public class OperationStep<T> : IOperation
{
/// <summary>
/// Denotes the Logical Name of the current operation
/// </summary>
public string Name { get; set; }
/// <summary>
/// Denotes the stage of execution of the current operation: Setup, Execution, Validation, Cleanup
/// </summary>
public OperationStage Stage { get; set; }
/// <summary>
/// Denotes whether the test step completed properly or failed.
/// </summary>
public OperationResult Result { get; set; }
/// <summary>
/// Denotes the return type of the test method.
/// </summary>
public T Context { get; set; }
/// <summary>
/// Denotes any other relevant information about the test step
/// </summary>
public string Description { get; set; }
/// <summary>
/// If the test step result is failed, this should have the stack trace and the error message.
/// </summary>
public string Error { get; set; }
}
The method calls themselves are a bit bloated and tedious but here is a sample.
public class DomainContext
{
private static volatile DomainContext currentContext;
private static object synchronizationToken = new object();
/// <summary>
/// default ctor.
/// </summary>
private DomainContext() { }
/// <summary>
/// Retrieves the Current DomainContext instance.
/// </summary>
public static DomainContext Current
{
get
{
if (currentContext == null)
{
lock (synchronizationToken)
{
if (currentContext == null)
{
currentContext = new DomainContext();
}
}
}
return currentContext;
}
}
/// <summary>
/// Establishes a connection to the domain.
/// </summary>
/// <param name="domainInfo"></param>
/// <returns></returns>
public OperationStep<PrincipalContext> GetPrincipalContext(DomainInfo domainInfo)
{
OperationStep<PrincipalContext> result = new OperationStep<PrincipalContext>();
result.Name = "Establish Connection to Active Directory";
result.Result = OperationResult.Success;
result.Stage = OperationStage.Setup;
result.Description = string.Format("Domain Name: {0}, Default Containter: {1}", domainInfo.FQDN, domainInfo.Container);
try
{
ContextType contextType = this.GetContextType(domainInfo.DomainType);
PrincipalContext principalContext;
try
{
principalContext = new PrincipalContext(contextType, domainInfo.FQDN, domainInfo.Container);
}
catch
{
throw new Exception("Unable to establish connection to Active Directory with the specified connection options.");
}
if (principalContext != null)
{
bool authenticationResult = principalContext.ValidateCredentials(domainInfo.Username, domainInfo.Password);
if (!authenticationResult)
{
throw new Exception("Unable to authenticate domain admin user to Active Directory.");
}
result.Context = principalContext;
result.Result = OperationResult.Success;
}
}
catch(Exception ex)
{
result.Error = ex.Message;
result.Result = OperationResult.Failure;
}
return result;
}
}
When all method calls have executed theoreticaly I should have an IEnumerable<IOperation> which in the case of a win form I can write in a csv file (to be viewed in MS Excel) or in the case of a unit test I can simply omit the extra info and ignore (other than the method executed successively and the T Context property).
If I understood you correctly - all that OperationSteps are here only for logging. Then why not enable simple .NET logging? Log needed info where it is convenient for you. You can use TraceSource with DelimetedTraceListener to write to .csv file. More than that. You can move logging logic to Strategy class and override its logging methods in your unit test so that instead of logging you call Assert methods.
My question is very similar to this issue: AntiForgery Exception: A required anti-forgery token was not supplied or was invalid
but I have the MVC3 and I using Razor installed.
controller has the
[ValidateAntiForgeryToken]
specified
in html is printed <input name="__RequestVerificationToken"... using #Html.AntiForgeryToken()
Also I observed, that if I remove the Authorization cookie in the browser, and controller method does not have [Authorize] I don't have any problems with AntiForery. Why?
Check your cookies and make sure that you are seeing the requestVerificationToken cookie being set correctly. I have run into this before where the cookies for the site were all set to be SSL only and I was trying to run it over regular HTTP locally, so the cookie was never being accepted because it was being transmitted over unsecure channels.
For me, this meant changing a line in the web.config under system.web/httpCookies to requireSSL="false"... but if this isn't what you are seeing, I would still look at things that might be messing with your cookies in the system (e.g. session resets, manually clearing the cookies somewhere, etc.). If you have the validation attribute on the controller methods correctly, and are still getting this, it is likely due to something modifying or removing that cookie!
Edit: Also, if you have this on the controller instead of only on the POST methods, that would be why... This is only applicable to form POSTs to the server.
Here's a simple custom version that you CAN apply to the form that will automatically validate on ALL POST action methods:
/// <summary>
/// Custom Implementation of the Validate Anti Forgery Token Attribute.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CustomValidateAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
/// <summary>
/// The ValidateAntiForgeryTokenAttribute.
/// </summary>
private readonly ValidateAntiForgeryTokenAttribute _validator;
/// <summary>
/// The AcceptVerbsAttribute.
/// </summary>
private readonly AcceptVerbsAttribute _verbs;
/// <summary>
/// Initializes a new instance of the <see cref="CustomValidateAntiForgeryTokenAttribute"/> class.
/// </summary>
/// <param name="verbs">The verbs.</param>
public CustomValidateAntiForgeryTokenAttribute(HttpVerbs verbs) : this(verbs, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomValidateAntiForgeryTokenAttribute"/> class.
/// </summary>
/// <param name="verbs">The verbs.</param>
/// <param name="salt">The salt.</param>
public CustomValidateAntiForgeryTokenAttribute(HttpVerbs verbs, string salt)
{
_verbs = new AcceptVerbsAttribute(verbs);
_validator = new ValidateAntiForgeryTokenAttribute
{
Salt = salt
};
}
/// <summary>
/// Called when authorization is required.
/// </summary>
/// <param name="filterContext">The filter context.</param>
public void OnAuthorization(AuthorizationContext filterContext)
{
var httpMethodOverride = filterContext.HttpContext.Request.GetHttpMethodOverride();
var found = false;
foreach (var verb in _verbs.Verbs)
{
if (verb.Equals(httpMethodOverride, StringComparison.OrdinalIgnoreCase))
{
found = true;
}
}
if (found && !filterContext.RequestContext.RouteData.Values["action"].ToString().StartsWith("Json"))
{
_validator.OnAuthorization(filterContext);
}
}
}
Then you can just add the following to all of your controllers, or to your base controller if you override and inherit from one:
[CustomValidateAntiForgeryToken(HttpVerbs.Post)]
Anti forgery token is tied to the user identity. If you changing currently logged in user identity between generating and validating tokens then token will not be validated successfully. Also, that explains why everything is working for you in anonymous mode.