Skip to content

Commit

Permalink
#6 privilege check on modification implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
marcwittke committed Aug 28, 2017
1 parent 0755f8c commit 7242d24
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 1 deletion.
5 changes: 5 additions & 0 deletions demo/DemoBlog.Application/Domain/BlogAuthorization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,10 @@ public bool CanCreate(Blog blog)
var claimsIdentity = identity as ClaimsIdentity;
return claimsIdentity != null && claimsIdentity.HasClaim(claim => claim.Type == claimsIdentity.RoleClaimType && claim.Value == "Admin");
}

public bool CanModify(Blog t)
{
return CanCreate(t);
}
}
}
5 changes: 5 additions & 0 deletions demo/DemoBlog.Application/Domain/BloggerAuthorization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,10 @@ public bool CanCreate(Blogger t)
var claimsIdentity = identity as ClaimsIdentity;
return claimsIdentity != null && claimsIdentity.HasClaim(claim => claim.Type == claimsIdentity.RoleClaimType && claim.Value == "Admin");
}

public bool CanModify(Blogger t)
{
return CanCreate(t);
}
}
}
5 changes: 5 additions & 0 deletions demo/DemoBlog.Application/Domain/PostAuthorization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,10 @@ public bool CanCreate(Post post)
var claimsIdentity = identity as ClaimsIdentity;
return claimsIdentity != null && claimsIdentity.HasClaim(claim => claim.Type == "urn:demoblog:blogadmin" && claim.Value == "blogId");
}

public bool CanModify(Post t)
{
return CanCreate(t);
}
}
}
34 changes: 33 additions & 1 deletion src/Backend.Fx.EfCorePersistence/EfRepository.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,58 @@
namespace Backend.Fx.EfCorePersistence
{
using System.Linq;
using System.Security;
using BuildingBlocks;
using Environment.MultiTenancy;
using Logging;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Patterns.Authorization;

public class EfRepository<TAggregateRoot> : Repository<TAggregateRoot> where TAggregateRoot : AggregateRoot
{
private static readonly ILogger Logger = LogManager.Create<EfRepository<TAggregateRoot>>();
private readonly DbContext dbContext;
private readonly IAggregateRootMapping<TAggregateRoot> aggregateRootMapping;

private readonly IAggregateAuthorization<TAggregateRoot> aggregateAuthorization;

public EfRepository(DbContext dbContext, IAggregateRootMapping<TAggregateRoot> aggregateRootMapping,
TenantId tenantId, IAggregateAuthorization<TAggregateRoot> aggregateAuthorization)
: base(tenantId, aggregateAuthorization)
{
this.dbContext = dbContext;
this.aggregateRootMapping = aggregateRootMapping;
this.aggregateAuthorization = aggregateAuthorization;

// somewhat hacky: using the internal EF Core services against advice
var localViewListener = dbContext.GetService<ILocalViewListener>();
localViewListener.RegisterView(AuthorizeChanges);
}

/// <summary>
/// Due to the fact, that a real lifecycle hook API is not yet available (see issue https://github.com/aspnet/EntityFrameworkCore/issues/626)
/// we are using an internal service to achieve the same goal: When a state change occurs from unchanged to modified, and this repository is
/// handling this type of aggregate, we're calling IAggregateAuthorization.CanModify to enforce user privilege checking.
///
/// We're accepting the possible instability of EF Core internals due to the fact that there is a full API feature in the pipeline that will
/// make this workaround obsolete. Also, not much of an effort was done to write this code, so if we have to deal with this issue in the future
/// again, we do not loose a lot.
/// </summary>
/// <param name="entry"></param>
/// <param name="previousState"></param>
private void AuthorizeChanges(InternalEntityEntry entry, EntityState previousState)
{
if (previousState == EntityState.Unchanged && entry.EntityState == EntityState.Modified && entry.EntityType.ClrType == typeof(TAggregateRoot))
{
var aggregateRoot = (TAggregateRoot) entry.Entity;
if (!aggregateAuthorization.CanModify(aggregateRoot))
{
throw new SecurityException($"You are not allowed to modify {AggregateTypeName}[{aggregateRoot.Id}]");
}
}
}

protected override void AddPersistent(TAggregateRoot aggregateRoot)
{
Logger.Debug($"Persistently adding new {AggregateTypeName}");
Expand Down
5 changes: 5 additions & 0 deletions src/Backend.Fx/Patterns/Authorization/AllowAll.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,10 @@ public bool CanCreate(TAggregateRoot t)
{
return true;
}

public bool CanModify(TAggregateRoot t)
{
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ public interface IAggregateAuthorization<TAggregateRoot> where TAggregateRoot :
/// Both overloads must return <code>true</code> to be considered as permission alloewance.
/// </summary>
bool CanCreate(TAggregateRoot t);

bool CanModify(TAggregateRoot t);
}
}

0 comments on commit 7242d24

Please sign in to comment.