Skip to content
This repository was archived by the owner on Dec 11, 2024. It is now read-only.

Optional Feature: Authorization

Marc Wittke edited this page Oct 5, 2022 · 2 revisions

The feature "Authorization" obligates you the implementation of an IAuthorizationPolicy<TAggregateRoot> for every IAggregateRoot type in your assemblies. Instances of these policy classes are applied to the repositories, so that on every read or write operation on it, the policy is automatically enforced.

Denied reads won't fail but just make the aggregate appear invisible, while a denied write throws a ForbiddenException.

Dependencies

This feature implicitly depends on the persistence feature, more specific on a persistence implementation that provides IQueryable<TAggregateRoot>s to the repositories.

Examples

build an application with authorization:

var application = new BackendFxApplication(
    new SimpleInjectorCompositionRoot(),
    new ExceptionLogger(Logger),
    GetType().Assembly);

application.EnableFeature(new PersistenceFeature(new InMemoryPersistenceModule<int>()));
application.EnableFeature(new AuthorizationFeature());

await application.BootAsync();

implement policies

As starting point, you can use DenyAll<T> or AllowAll<T> as base class and start overriding single methods. Feel free to use injection to access the current identity when constructing the filter expressions or authorization policies. The lifetime of a policy instance is scoped so that each invocation will get the current identity again.

public class CustomersPolicy : AllowAll<Customer, int> 
{
    private readonly ICurrentTHolder<IIdentity> _identityHolder;

    // injection is supported
    public CustomersPolicy(ICurrentTHolder<IIdentity> identityHolder) 
    {
          _identityHolder = identityHolder;
    }

    // make sure that this can be understood by your persistence layer
    public override Expression<Func<Agg, bool>> HasAccessExpression =>
        agg => _identityHolder.Current is SystemIdentity || agg.OwnerId = _identityHolder.Current.GetId();
   
    public override bool CanCreate(Agg t)
    {
        // at least authenticated (or whatever your business rules need)
        return !(_identityHolder.Current is AnonymousIdentity);
    }
}

see it in action

await application.Invoker.InvokeAsync(async sp =>
{
    // the repository will get a scoped instance of our policy class injected
    var repository = sp.GetRequiredService<IRepository<Customer, int>>();

    // this will return empty even if there would be items since we're invoking as AnonymousIdentity
    var customers = await repository.GetAllAsync();

    // this will throw a ForbiddenException, since we're invoking as AnonymousIdentity
    await repository.AddAsync(new Customer(1000, "ACME Inc.")));

}, new AnonymousIdentity());
Clone this wiki locally