-
Notifications
You must be signed in to change notification settings - Fork 1
Optional Feature: Authorization
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
.
This feature implicitly depends on the persistence feature, more specific on a persistence implementation that provides IQueryable<TAggregateRoot>
s to the repositories.
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();
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);
}
}
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());