Skip to content

Commit

Permalink
#11 TenantManager.GetTenant by id
Browse files Browse the repository at this point in the history
MetroPoliPlan/mep-backend#353 Delete vs. Authorization
  • Loading branch information
marcwittke committed Dec 24, 2017
1 parent 4217c90 commit 957c553
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/Backend.Fx/BuildingBlocks/Repository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,11 @@ public TAggregateRoot[] GetAll()

public void Delete(TAggregateRoot aggregateRoot)
{
if (aggregateRoot.TenantId != tenantId.Value)
if (aggregateRoot.TenantId != tenantId.Value || !aggregateAuthorization.CanDelete(aggregateRoot))
{
throw new System.Security.SecurityException($"You are not allowed to delete {typeof(TAggregateRoot).Name}[{aggregateRoot.Id}]");
}

DeletePersistent(aggregateRoot);
}

Expand Down
12 changes: 12 additions & 0 deletions src/Backend.Fx/Environment/MultiTenancy/ITenantManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public interface ITenantManager
{
TenantId[] GetTenantIds();
Tenant[] GetTenants();
Tenant GetTenant(TenantId id);
bool IsActive(TenantId tenantId);
TenantId CreateDemonstrationTenant(string name, string description, bool isDefault);
TenantId CreateProductionTenant(string name, string description, bool isDefault);
Expand Down Expand Up @@ -63,6 +64,17 @@ protected void InitializeTenant(Tenant tenant)
public abstract TenantId[] GetTenantIds();

public abstract Tenant[] GetTenants();
public Tenant GetTenant(TenantId tenantId)
{
Tenant tenant = FindTenant(tenantId);

if (tenant == null)
{
throw new ArgumentException($"Invalid tenant Id [{tenantId.Value}]", nameof(tenantId));
}

return tenant;
}

public bool IsActive(TenantId tenantId)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,10 @@ public virtual bool CanModify(TAggregateRoot t)
{
return true;
}

public bool CanDelete(TAggregateRoot t)
{
return CanModify(t);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,11 @@ public interface IAggregateAuthorization<TAggregateRoot> where TAggregateRoot :
/// This overload is called directly before saving modification of an instance, so that you can use the instance's state for deciding.
/// </summary>
bool CanModify(TAggregateRoot t);

/// <summary>
/// Implement a guard that might disallow deleting an existing aggregate.
/// This overload is called directly before saving modification of an instance, so that you can use the instance's state for deciding.
/// </summary>
bool CanDelete(TAggregateRoot t);
}
}
18 changes: 18 additions & 0 deletions tests/Backend.Fx.Tests/BuildingBlocks/TheRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,23 @@ public void ThrowsOnDeleteWhenTenantIdHolderIsEmpty()
Assert.Throws<InvalidOperationException>(() => sut.Delete(agg1));
}

[Fact]
public void ThrowsOnDeleteWhenUnauthorized()
{
var authorization = A.Fake<IAggregateAuthorization<TheAggregateRoot.TestAggregateRoot>>();
A.CallTo(() => authorization.HasAccessExpression).Returns(agg => true);
A.CallTo(() => authorization.Filter(A<IQueryable<TheAggregateRoot.TestAggregateRoot>>._)).ReturnsLazily((IQueryable<TheAggregateRoot.TestAggregateRoot> q) => q);
A.CallTo(() => authorization.CanCreate(A<TheAggregateRoot.TestAggregateRoot>._)).Returns(true);
A.CallTo(() => authorization.CanDelete(A<TheAggregateRoot.TestAggregateRoot>._)).Returns(false);

var sut = new InMemoryRepository<TheAggregateRoot.TestAggregateRoot>(new InMemoryStore<TheAggregateRoot.TestAggregateRoot>(), new TenantId(234), authorization);

var agg1 = new TheAggregateRoot.TestAggregateRoot(12123123, "whatever") { TenantId = 234 };
sut.Store.Add(agg1.Id, agg1);

Assert.Throws<SecurityException>(() => sut.Delete(agg1));
}

[Fact]
public void ReturnsEmptyWhenTenantIdHolderIsEmpty()
{
Expand Down Expand Up @@ -212,6 +229,7 @@ public void DeletesItemFromMyTenant()
A.CallTo(() => authorization.HasAccessExpression).Returns(agg => true);
A.CallTo(() => authorization.Filter(A<IQueryable<TheAggregateRoot.TestAggregateRoot>>._)).ReturnsLazily((IQueryable<TheAggregateRoot.TestAggregateRoot> q) => q);
A.CallTo(() => authorization.CanCreate(A<TheAggregateRoot.TestAggregateRoot>._)).Returns(true);
A.CallTo(() => authorization.CanDelete(A<TheAggregateRoot.TestAggregateRoot>._)).Returns(true);

var sut = new InMemoryRepository<TheAggregateRoot.TestAggregateRoot>(new InMemoryStore<TheAggregateRoot.TestAggregateRoot>(), new TenantId(234), authorization);

Expand Down

0 comments on commit 957c553

Please sign in to comment.