dotnetjunkie / solidservices

Reference architecture application for .NET that demonstrates how to build highly maintainable web services.
MIT License
197 stars 25 forks source link

Implementing row-based security #4

Open dotnetjunkie opened 9 years ago

dotnetjunkie commented 9 years ago

This discussion was originally posted on CodePlex as discussion 566865 but that original discussion is not available any longer.

dotnetjunkie commented 6 years ago

[Originally posted by @teatime on Codeplex, thread #566865 | Message #1302762 | 2014-09-09]

I posted a question to Steven here, who kindly responded with an answer regarding the Command Pattern.

However, I am also interested if there are any other approaches to implement row based security and how to keep things SOLID.

So given a couple of scenarios in a multiple customer web based system with a shared database:

I pretty much have the second problem solved by storing the user account and organization IDs in the session and only using those values when writing to the database.

However, the first scenario gives rise to the question on where is the correct place to do this check, my feeling would be as close to the user as possible. For a system using a repository pattern you could do it:

The check for a property against a model/record(s) would essentially have to go off to the lookup repository/database (or a cache) and check if the user can access the lookup record in question, whether it be a simple check for if in their organization, or something more advanced whereby they can only access hierarchical data in their branch of the structure e.g. cost centre, department.

I think the core concept here is: don't trust anything that comes back from the client, especially record ID's.

One person suggested using a validator handler with the Command Pattern (see comments in link above) and that seems to make sense. I would also be interested to see an example of this or something similar if anyone has done it.

dotnetjunkie commented 6 years ago

[Originally posted by @qujck on Codeplex, Thread #566865 | Message #1302837 | 2014-09-09]

Hi @teatime

I have an event system that would support this. A generic decorator raises generic events before and after each command is executed. Adding logic before or after a command (or a bunch of commands) is simply a case of creating a new event subscriber.

(Please note I'm typing some of this into the browser so please excuse typos etc)

Infrastructure components:

I have an IEventPublisher (and a separate ISubscriptionService to minimise the dependency on the container)

public interface IEventPublisher {
    void Publish<T>(T eventMessage) where T : class, IEvent;
}

public interface ISubscriptionService {
    IEnumerable<ISubscriber<T>> GetSubscriptions<T>() where T : IEvent;
}

And a simple ISubscriber that any event subscriber must implement

public interface ISubscriber<TEvent> where TEvent : IEvent
{
    void Handle(TEvent eventMessage);
}

Here are 2 events for commands. You can add events for many common events such as Add/Update/Delete etc.

public class OnBeforeHandle<TCommand> : IEvent where TCommand : ICommand {
    public OnBeforeHandle(Tcommand command) {
        this.Command = command;
    }

    public TCommand Command { get; private set; }
}

public class OnAfterHandle<TCommand> : IEvent where TCommand : ICommand {
    public OnAfterHandle(Tcommand command) {
        this.Command = command;
    }

    public TCommand Command { get; private set; }
}

And a single decorator to wrap all command handlers to all raise the events

public sealed class CommandHandlerEventDecorator<TCommand> where TCommand : class, ICommand {
    private readonly ICommandHandler<TCommand> decorated;
    private readonly IEventPublisher eventPublisher;

    public CommandHandlerEventDecorator(
        ICommandHandler<TCommand> decorated, IEventPublisher eventPublisher) {
        this.decorated = decorated;
        this.eventPublisher = eventPublisher;
    }

    public override void Handle(TCommand command) {
        var onBefore = new OnBeforeHandle<TCommand>(command);
        this.eventPublisher.Publish(onBefore);
        this.decorated.Handle(onBefore.Command);
        var onAfter = new OnAfterHandle<TCommand>(onBefore.Command);
        this.eventPublisher.Publish(onAfter);
    }
}

The event publisher publishes the event instance to each subscriber

public sealed class EventPublisher : IEventPublisher
{
    private readonly ISubscriptionService subscriptionService;

    public EventPublisher(ISubscriptionService subscriptionService)
    {
        this.subscriptionService = subscriptionService;
    }

    public void Publish<TEvent>(TEvent eventMessage)
        where TEvent : class, IEvent
    {
        var subscriptions = this.subscriptionService.GetSubscriptions<TEvent>().ToList();
        subscriptions.ForEach(x => this.PublishToSubscriber(x, eventMessage));
    }

    private void PublishToSubscriber<TEvent>(ISubscriber<TEvent> x, TEvent eventMessage)
        where TEvent : class, IEvent
    {
        x.Handle(eventMessage);
    }
}

The subscribers are fetched from the container (so registration details such as lifetimes and decorators are applied)

public sealed class EventSubscriptions : ISubscriptionService
{
    private readonly Container container;

    public EventSubscriptions(Container container)
    {
        this.container = container;
    }

    public IEnumerable<ISubscriber<T>> GetSubscriptions<T>()
        where T : IEvent
    {
        return this.container.GetAllInstances<ISubscriber<T>>();
    }
}

A subscriber

public sealed class MyEntity_OnBeforeCommand_Block :
    ISubscriber<OnBeforeCommand<MyEntity>>
{
    public void Handle(OnBeforeCommand<MyEntity> onBefore)
    {
        throw new AccessViolationException("Not allowed");
    }
}

Registration

If you know Simple Injector you'll probably know that all these subscribers can be registered in one line and therefore new subscribers should automatically be found during subscriber registration:

container.RegisterManyForOpenGeneric(
    typeof(ISubscriber<>),
    container.RegisterAll,
    AppDomain.CurrentDomain.GetAssemblies());

Summary

Adding events is really easy once this infrastructure is in place. Here we have around 200 hand coded subscribers plus many more auto-generated ones throughout the solution. Each subscriber is small, does one thing and is easily unit tested.

dotnetjunkie commented 6 years ago

[Originally posted by @Baldman on Codeplex, Thread #566865 | Message #1302837 | 2014-09-09]

That's an elegant solution right there ^^

Regarding the approach i described in the comments of Steven's blog post, here is an example of something that validates the execution of each command against the current user. (bear in mind that this is overkill if you can authorise at the controller level)....

public class CommandAuthorizer<TCommand> : ICommandHandler<TCommand> 
    where TCommand : CommandBase
{
    private readonly ICommandPermissionResolver _permissionResolver;
    private readonly ICommandAuditor _commandAuditor;
    private readonly ICommandHandler<TCommand> _decoratee;
    private readonly IPrincipal _currentUser;

    public CommandAuthorizer(
        ICommandPermissionResolver permissionResolver,
        ICommandAuditor commandAuditor,
        ICommandHandler<TCommand> decoratee,
        IPrincipal currentUser)
    {
        _permissionResolver = permissionResolver;
        _commandAuditor = commandAuditor;
        _decoratee = decoratee;
        _currentUser = currentUser;
    }

    public void Handle(TCommand command)
    {
        if (_permissionResolver.Resolves(typeof(TCommand), currentUser))
        {
            //pass invocation on to the actual handler
            _decoratee.Handle(command);
            return;
        }

        // Current user is not permitted to execute this command,
        // log and throw exception
        _commandAuditor.Audit(command, AuditType.AuthorisationFailure);
        throw new CommandAuthorisationException(typeof(TCommand), _currentUser);
    }
}

That deals with authorisation of all types of command. The ICommandPermissionResolver maintains a list of all command types, and which users are allowed to execute them. You would typically populate the list of command types via reflection at application startup, and obtain a list of permitted commands for the user at logon.

However, YMMV. It all depends on the business requirements and how granular they expect authorisation to be. Ideally you would deal with authorisation at the controller level if it fits with requirements.

For specific command custom validation, just create a handler that decorates that specific command type. If i had a CreateContact command, and a CreateContactHandler for it, you could do custom validation like so....

public class CreateContactHandlerAuthorizer<CreateContactCommand>
    : ICommandHandler<CreateContactCommand>
{
    private readonly ICommandHandler<CreateContactCommand> _decoratee;

    public CreateContactHandlerAuthorizer(ICommandHandler<TCommand> decoratee)
    {
        _decoratee = decoratee;
    }

    public void Handle(CreateContactCommand command)
    {
        // TODO check command values as you describe in your original post 
        // and throw exception if there are issues.

        //pass invocation on to the actual handler
        _decoratee.Handle(command);
    }
}

They are both examples done in a text editor, so hopefully should compile :)

Be interested to hear others thoughts and insights on the subject.

dotnetjunkie commented 6 years ago

[Originally posted by @dotnetjunkie on Codeplex, Thread #566865 | Message #1302895 | 2014-09-09

In the application I'm working on right now, we have two levels of security validation:

Permission based security validation:

Users can have multiple roles and a role is a set of permissions. In code we mark stuff with a permission. The buttons on the screen are rendered based on the permissions of the user, and our queries and commands are marked with a custom permission attribute and we have decorators that check whether the operation is allowed (pretty much like @Baldman's answer). Here's an example:

[Permission(Permissions.Administration.CreateUsers)]
public class AddUserCommand : ICommand
{
    [Required, StringLength(100)] public string LoginName { get; set; }
    [Required] public Guid[] AllowedRoles { get; set; }
}

Permissions.Administration.CreateUsers is a constant string containing the Guid Id of the permission (it's a string because Guids can't be constants and therefore can't be used in attributes directly).

The decorator that verifies the permissions looks like this:

public sealed class PermissionCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand> 
    where TCommand : ICommand
{
    private readonly MessagePermissionChecker<TCommand> permissionChecker;
    private readonly ICommandHandler<TCommand> decoratee;

    public PermissionCommandHandlerDecorator(
        MessagePermissionChecker<TCommand> permissionChecker,
        ICommandHandler<TCommand> decoratee)
    {
        this.decoratee = decoratee;
        this.permissionChecker = permissionChecker;
    }

    public void Handle(TCommand command)
    {
        this.permissionChecker.CheckPermissionForCurrentUser();
        this.decoratee.Handle(command);
    }
}

The MessagePermissionChecker<T> looks like this:

public sealed class MessagePermissionChecker<TMessage>
{
    private static readonly Guid permissionId;
    private readonly IUserPermissionChecker permissionChecker;

    static MessagePermissionChecker()
    {
        var permissionAttribute =
            typeof(TMessage).GetCustomAttribute<PermissionAttribute>();
        if (permissionAttribute == null)
        {
            throw new InvalidOperationException(string.Format(
                "The type {0} is not marked with the [{1}]. Please define the " +
                "permission the user needs to execute this query",
                typeof(TMessage).FullName, typeof(PermissionAttribute).Name));
        }
        permissionId = permissionAttribute.PermissionId;
    }

    public MessagePermissionChecker(IUserPermissionChecker permissionChecker)
    {
        this.permissionChecker = permissionChecker;
    }

    public void CheckPermissionForCurrentUser()
    {
        this.permissionChecker.CheckPermission(permissionId);
    }
}

The same construct we have for queries. In case the user doesn't have permissions, an exception is thrown. For other parts of the system we query and filter based on the user's permissions. For instance, many user screens show 'related items'. Those related items are small lists that contain information that is related to the main information on the screen. The system contains an IRelatedInfoProvider<T> abstraction and implementations are marked with the [Permission] attribute. Based on the permissions of the user those implementations are applied to that screen or not.

Row-based security validation and filtering:

Our system contains a few mechanisms for row-based security. First of all, we use an IAuthorizedRepository<TEntity> abstraction. This abstraction contains both a TEntity GetById(Guid) and IQueryable<TEntity> GetAll() method. The GetById method throws an exception if an entity is requested that can't be accessed due to the user's rights. The GetAll returns a list of entities that is filtered based on the user's permissions.

Filtering is not done in the IAuthorizedRepository<TEntity> implementation (we only have one generic implementation). Filtering is delegated to a different abstraction, namely IEntityAuthorizationFilter<TEntity>. Not mixing the two was a bit cleaner in our case. It is the specific IEntityAuthorizationFilter<TEntity> implementation that controls which entities are returned from the repository and when the GetById throws an exception. Since each entity in the system has different row-based authorization rules, we have filter implementations for all our main entities. Most entities are directly or indirectly related to a business unit and users can have access to one or multiple business units. So most implementations filter on the current user's permitted business units, but sometimes other checks are performed as well.

To secure the system, it would often be enough to inject an IAuthorizedRepository<TEntity> into a command handler, but this makes the security checks very implicit and a security review should analyze the complete command handler. So we chose to have a separate abstraction for security validations: ISecurityValidator<T>. We've got implementations for both queries and commands. To ensure that a user doesn't pass in an id to something it shouldn't have access to, it is often the security validator implementation that checks this. Often such implementation simply delegates to the authorization repository and this will throw an exception in case a certain entity can't be retrieved. Sometimes however, validations are very specific to the command or query. Our system is modular and one particular module (that contains cross-module information, such as managing documents and images for entities or entities of several modules in the system) has not enough information to do the verification itself. Inside a security validator, that module will send a request throughout the system for the validation of a certain operation that is about to be performed. Such request could be something like: "is the current user allowed to view documents for the entity with id X".

Often, however, many commands and queries need the same validation. For instance, many commands and queries can be related to one specific entity. To prevent having to duplicate those validations we mark our commands and queries with interfaces such as this IRequireAccessToAsset:

[Permission(Permissions.AssetManagement.ManageAssetData)]
public sealed class UpdateAssetCommand : ICommand, IRequireAccessToAsset
{
    public Guid AssetId { get; set; }
    ...
}

By marking the command with an IRequireAccessToAsset we tell the system to check to see if the current user has access to the asset. We have one generic ISecurityValidator<T> implementation with a type constraint on IRequireAccessToAsset:

public sealed class RequireAccessToAssetSecurityValidator<T> : ISecurityValidator<T>
    where T : IRequireAccessToAsset
{
    private readonly IAuthorizedRepository<Asset> assetRepository;

    public RequireAccessToAssetSecurityValidator(
        IAuthorizedRepository<Asset> assetRepository)
    {
        this.assetRepository = assetRepository;
    }

    public void Check(T instance)
    {
        // We delegate the security checking to the asset repository here.
        this.assetRepository.GetById(instance.AssetId);
    }
}

We use the same [Permission] attribute on the controller level as well. Although these attributes are redundant from a technical perspective (because these checks are already performed in the business layer), we found that it caused stress to our users and testers to see that they were still allowed to access a certain url. So we decided to directly block access to the controller as well, with the downside of this bit of code duplication. But as always, YMMV.

dotnetjunkie commented 6 years ago

[Originally posted by @teatime on Codeplex, Thread #566865 | Message #1303228 | 2014-09-10]

@dot_NET_Junkie

Regarding your role/permission model, I have used pretty much that model for multiple applications, usually following this pattern:

(This is one tab for a Visio diagram for a developed system, so a few extra details to ignore)

Depending on whether the Roles are configurable by organisation or not we have an OrganisationID column in the Role table. A permission could literally be anything e.g. View, Control, Menu, Report, etc. and the relevant entity will have a PermissionID against it.

Because of this granularity, if we do allow Roles to be customised, we further group Permissions so that a user can assign multiple Permissions to a role at once e.g. by responsibility: RoleResponsibility <> Responsibility <> ResponsibilityPermission <> Permission

The UserAccountPermission allows individual permissions to be assigned to a user (or in some cases marked as Disallowed to override role permissions), typically this model is used for user generated content e.g. Reports, and allows them to be shared with other users/roles.

And typically as @Baldman suggests we do permission validation against the controller and so far this has taken the form of a AuthorizeAttribute, however, something always felt bad with this, mainly due to lack of Constructor Injection and I think after stumbling across your article, I have now started reviewing a whole heap of design decisions made to meet deadlines.

Which is how I ended up looking at the Command Pattern, and how the hell do row security when creating and updating records.

Read and deleting is handled but SQL joins, which seems to be the most efficient way of doing things.

dotnetjunkie commented 6 years ago

[Originally posted by @Wayne on Codeplex, Thread #566865 | Message #1318305 | 2014-11-01]

@dot_NET_Junkie

More on row based security and validation.

You show you have ISecurityValidator<T> and it uses IRequireAccessToAsset to perform row level validation. In this case you show it handing that off to your AuthorizedRepository.

It has a Check() method - how is all that hooked up to your command exactly? Are you just injecting it and your command is calling the Check() method?

dotnetjunkie commented 6 years ago

[Originally posted by @dotnetjunkie on Codeplex, Thread #566865 | Message #1318332 | 2014-11-02]

Hi Wayne,

This security checking is a cross cutting concern, so we shouldn't complicate our command handlers with this. We implement this with a decorator. The decorator calls the Check method.

dotnetjunkie commented 6 years ago

[Originally posted by @??? on Codeplex, Thread #566865 | Message #1318342 | 2014-11-02

Wayne,

The PermissionCommandHandlerDecorator class in the example above is the decorator that "hooks" the Check() method to command handlers. See @dot_NET_Junkie's article here for more info on decorating command handlers.

dotnetjunkie commented 6 years ago

[Originally posted by @wayne on Codeplex, Thread #566865 | Message #1318381 | 2014-11-02]

Oh you are decorating your IAuthorizedRepository? If so, then all your checks are going being delegated to the IAuthorizedRepository?

dotnetjunkie commented 6 years ago

[Originally posted by @dotnetjunkie on Codeplex, Thread #566865 | Message #1318387 | 2014-11-02]

Nope. We did that initially, but that didn't feel right. We wanted to make clear from the abstraction that authorization checks were applied, so that's why we called it IAuthorizationRepository. But having an implementation of that interface that doesn't do any authorization checks was just weird (it didn't do what it promised to do). So that was when we introduced the IAuthorizationFilter<T> abstraction. The DbContextAuthorzationRepository<T> now depends on IAuthorizationFilter<T> and uses that to ensure the right authorization checks are applied.

So there is currently no decorator for IAuthorizedRepository. There might be in the future though, for logging, audit trailing, caching, or who knows.

dotnetjunkie commented 6 years ago

[Originally posted by @Wayne on Codeplex, Thread #566865 | Message #1318456 | 2014-11-02]

The RequireAccessToAssetSecurityValidator is not a decorator for an IQuery or an IComamnd. It is written above as if it is a decorator of the IAuthorizedRepository<>. So, I am confused as to how you have it all setup.

So am 100% with you on the permission based checker with an attribute, I do the same thing. The question is around the row based security.

You say you have an IAuthorizedRepository<> and that delegates permission checking to IEntityAuthorizationFilter<>. That is fine and simple. Normally, like you said, you could just pass an IAuthorizedRepository<> into the command/query and be fine.

However, you selected to have a SecurityValidator<T> that would be hooked up based on the command/query implementing an interface. However, that is not shown. The SecurityValidator<> class you gave an example of looks like an IAuthorizedRepository<> decorator.

Could you share more about how you did this row validation, this is something I am trying to implement and just curious how you did it.

Again, I am really not confused by the PermissionCommandHandlerDecorator or how that works...it is more the 'row level security filtering' part of the above that is not clear.

Thanks for your input!

dotnetjunkie commented 6 years ago

[Originally posted by @dotnetjunkie on Codeplex, Thread #566865 | Message #1319931 | 2014-11-07]

In our system we got two decorators for security.

Our first line of defense is the PermissionCommandHandlerDecorator<T> and PermissionQueryHandlerDecorator<T> for permission checks. Those decorators check to see whether the current user has the correct permissions. Queries and command are marked with a [Permission] attribute. Such attribute is required. The decorators will throw an exception if such attribute is missing. Simply because we (the developers) easily forget to apply such attribute.

The second line of define are the SecurityCommandHandlerDecorator<T> and SecurityQueryHandlerDecorator<T> decorators. Those decorators ensure row-based security. Here's our SecurityCommandHandlerDecorator<T>:

public sealed class SecurityCommandHandlerDecorator<TCommand> 
    : ICommandHandler<TCommand>
    where TCommand : ICommand
{        
    private readonly ICommandHandler<TCommand> decoratee;
    private readonly ISecurityValidator<TCommand> securityValidator;

    public SecurityCommandHandlerDecorator(
        ICommandHandler<TCommand> decoratee,
        ISecurityValidator<TCommand> securityValidator)
    {
        this.decoratee = decoratee;
        this.securityValidator= securityValidator;
    }

    public void Handle(TCommand command)
    {
        this.securityValidator.Check(command);
        this.decoratee.Handle(command);
    }
}

As you can see, the decorator itself does nothing more than delegating the security checking to the ISecurityValidator<T>. The SecurityQueryHandlerDecorator<T> counterpart does exactly the same. The ISecurityValidationProvider<T> implementation looks like this:

// ISecurityValidator is defined with the 'in' attribute to allow more-complex
// validation scenarios.
public interface ISecurityValidator<in T>
{
    void Check(T instance);
}

public sealed  class SimpleInjectorSecurityValidatorComposite<T>
    : ISecurityValidator<T>
{
    private readonly IEnumerable<ISecurityValidator<T>> securityValidators;

    public SimpleInjectorSecuritySecurityValidatorComposite(
        IEnumerable<ISecurityValidator<T>> securityValidators)
    {
        this.securityValidators = securityValidators;
    }

    public void Check(T instance)
    {
        foreach (var validator in this.securityValidators)
        {
            validator.Check(instance);
        }
    }
}

The SimpleInjectorSecuritySecurityValidatorComposite<T> allows wrapping a large number of validators that are all applicable to the given T. Do note that if you use a different DI library, the approach will be different. Some containers make it very rather complex to register composites, and you might have to add an extra abstraction to get this working.

So this composite forwards the security checks to the registered ISecurityValidator<T> implementations. Here's such an implementation:

public sealed class RequireAccessToAssetSecurityValidator<T> : ISecurityValidator<T> 
    where T : IRequireAccessToAsset
{
    private readonly IAuthorizedRepository<Asset> assetRepository;

    public RequireAccessToAssetSecurityValidator(
        IAuthorizedRepository<Asset> assetRepository)
    {
        this.assetRepository = assetRepository;
    }

    public void Check(T instance)
    {
        // We delegate the security checking to the asset repository here.
        this.assetRepository.GetById(instance.AssetId);
    }
}

And we have multiple commands and queries that implement this IRequireAccessToAsset interface. And this implementation will get used by the SimpleInjectorSecuritySecurityValidatorComposite<T> for such command or query. Commands and queries might implement multiple interfaces, such as IRequireAccessToSalesOrder, IRequireAccessToCompany, etc.

This is how we currently do row-based security.

dotnetjunkie commented 6 years ago

[Originally posted by @wayne on Codeplex, Thread #566865 | Message #1327083 | 2014-11-28]

Ok, thanks for the information. Seems to make sense.

I have a couple of questions:

1) It looks like

public SecurityCommandHandlerDecorator(ICommandHandler<TCommand> decoratee,
        ISecurityValidator<TCommand> securityValidator) {

Would get RequireAccessToAssetSecurityValidator<T> injected into it as this matches the interface being injected and so does SimpleInjectorSecuritySecurityValidatorComposite<T>

Clearly in your case, you are saying SimpleInjectorSecuritySecurityValidatorComposite<T> gets injected and it makes a list of the other ones, but I am not sure why that injection is selected.

2) So, you have provided row based security for 'read' access. That is an important piece as it is the most common. However, there is the case of other row based security options, like 'does user have ability to do X on this row'. I suppose you could make an interface for every operation, but that would explode. Take a basic order system. You might have ability to view order, cancel order, credit order, edit order, etc - so the interfaces would go crazy. In each case need some row information on the order to determine if they can do that operation...

Thoughts?

dotnetjunkie commented 6 years ago

[Originally posted by @dotnetjunkie on Codeplex, Thread #566865 | Message #1327201 | 2014-11-29]

Wayne,

What we actually did was not to implement "row based security for 'read' access" but actually "row based security for access". There's a decorator for command handlers as well, so we check this security for writing as well. On top of this we have permissions and I think this is exactly what you need when you say "have ability to view order, cancel order, credit order, edit order, etc". These are all permissions. In our system we define permissions like ViewOrders, CancelOrders, CreateNewOrders, ManageOrderDetails. We use the [Permission] attributes on our commands, queries, controllers, and other artifacts to specify which permission the user needs to be able to access that code.

The only thing we don't do is row-level-permissions. So we grand or deny access on a row level per user, and have a set of permissions per user. So we can't say is that the user is allowed to view order 1 and cancel it, but not credit it, and is allowed to view order 2 and credit it, but not cancel it. This however is a scenario that we don't need in the system.

but I am not sure why that injection is selected.

I'm not sure what you mean by this and what you would like to know. So I'm unable to comment on that.

dotnetjunkie commented 6 years ago

[Originally posted by @jjwilliams on Codeplex, Thread #566865 | Message #1360439 | 2015-03-06]

This is an awesome thread - I've been following it for awhile now. I have used the ISecurityValidator<T>, but am unsure how to register it ... could you show your registrations?

I used Register(), but it still complains that nothing is registered

container.Register(typeof(ISecurityValidator<>), assemblies);
dotnetjunkie commented 6 years ago

[Originally posted by @dotnetjunkie on Codeplex, Thread #566865 | Message #1360445 | 2015-03-06]

Hi jjwilliams,

That's because Simple Injector differentiates the registration of collections from one-to-one mappings. So you need to explicitly register collections here. For instance:

container.Register(
    typeof(ISecurityValidator<>),
    typeof(SimpleInjectorSecurityValidatorComposite<>),
    Lifestyle.Singleton);

container.Collection.Register(typeof(ISecurityValidator<>), assemblies);

This however only registers all non-generic security validator implementations. The open-generic implementations can be appended to the collections as follows:

container.Collection.Append(typeof(ISecurityValidator<>), 
    typeof(RequireAccessToAssetSecurityValidator<>));
dotnetjunkie commented 6 years ago

[Originally posted by @??? on Codeplex, Thread #566865 | Message #1436228 | 2015-07-27]

We have decided to go back and apply some of these Permissions across our application as our permission checking is getting out of control. I am curious, you say that you use the Permission attribute on commands/queries and controllers, which makes sense because that is annoying for the user to see things they cannot act on.

But how does the Permission attribute hit the DB to check the actual permissions at the controller level? I don't believe you have constructor injection here.

I see how it works for Commands and Queries, IUserPermissionChecker [probably] hits the DB to check the permission. But how does it work on controllers?

dotnetjunkie commented 6 years ago

[Originally posted by @dotnetjunkie on Codeplex, Thread #566865 | Message #1436262 | 2015-07-28]

But how does the Permission attribute hit the DB to check the actual permissions at the controller level?

Well, that's simple: it doesn't. That attribute is simply a passive attribute. And it has to be passive, because it is used in the business layer as well. We don't want our business layer to depend on System.Web.Mvc or System.Web.Http.

The solution is simple, create specific IAuthorizationFilter and add that to the list of global filters:

class PermissionFilter : IAuthorizationFilter
{
    private Func<IUserPermissionChecker> factory;

    public PermissionFilter(Func<IUserPermissionChecker> factory) {
        this.factory = factory;
    }

    public void OnAuthorization(AuthorizationContext context) {
        var permissionChecker = this.factory.Invoke();

        var controllerAttributes = 
            context.ActionDescriptor.ControllerDescriptor.ControllerType
                .GetCustomAttributes<PermissionAttribute>(true);

        var actionAttributes = 
            context.ActionDescriptor.GetCustomAttributes<PermissionAttribute>(true);

        bool isUnauthorized = (
            from attribute in actionAttributes.Concat(controllerAttributes)
            where !permissionChecker.HasPermission(attribute.PermissionId)
            select attribute)
            .Any();

        if (isUnauthorized) {
            context.Result = new HttpUnauthorizedResult();
        }
    }
}