Skip to content

Commit

Permalink
Added missing support for instance-based authorizations.
Browse files Browse the repository at this point in the history
  • Loading branch information
gmcelhanon committed Oct 14, 2024
1 parent 10aee3c commit 5010905
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ public async Task AuthorizeEntityAsync(
}

// Get the authorization filtering information
var authorizationPlan = _dataManagementAuthorizationPlanFactory.CreateAuthorizationPlan(actionUri);
var authorizationPlan = _dataManagementAuthorizationPlanFactory.CreateAuthorizationPlan(
actionUri,
entity,
authorizationPhase);

_entityInstanceAuthorizationValidator.Validate(
authorizationPlan.RequestContext.Data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public DataManagementAuthorizationPlanFactory(
_resourceClaimUriProvider = resourceClaimUriProvider;
}

/// <inheritdoc cref="IDataManagementAuthorizationPlanFactory.CreateAuthorizationPlan(string)" />
public DataManagementAuthorizationPlan CreateAuthorizationPlan(string actionUri)
{
// Build the request context
Expand All @@ -46,26 +47,38 @@ public DataManagementAuthorizationPlan CreateAuthorizationPlan(string actionUri)
return CreateAuthorizationPlan(resource, actionUri);
}

/// <inheritdoc cref="IDataManagementAuthorizationPlanFactory.CreateAuthorizationPlan(EdFi.Ods.Common.Models.Resource.Resource,string)" />
public DataManagementAuthorizationPlan CreateAuthorizationPlan(Resource resource, string actionUri)
{
string[] resourceClaimUris = _resourceClaimUriProvider.GetResourceClaimUris(resource);
var apiClientContext = _apiClientContextProvider.GetApiClientContext();
var dataManagementRequestContext = CreateDataManagementRequestContext(resource, actionUri);

var dataManagementRequestContext = new DataManagementRequestContext(
apiClientContext,
resource,
resourceClaimUris,
actionUri,
(Type) (resource.Entity as dynamic).NHibernateEntityType);
return CreateAuthorizationPlan(dataManagementRequestContext);
}

// Get authorization filters
/// <inheritdoc cref="IDataManagementAuthorizationPlanFactory.CreateAuthorizationPlan(string,object,EdFi.Ods.Common.Security.Claims.AuthorizationPhase)" />
public DataManagementAuthorizationPlan CreateAuthorizationPlan(
string actionUri,
object entity,
AuthorizationPhase authorizationPhase)
{
// Build the request context
var resource = _dataManagementResourceContextProvider.Get().Resource;

var dataManagementRequestContext = CreateDataManagementRequestContext(resource, actionUri, entity, authorizationPhase);

return CreateAuthorizationPlan(dataManagementRequestContext);
}

private DataManagementAuthorizationPlan CreateAuthorizationPlan(DataManagementRequestContext dataManagementRequestContext)
{
var authorizationBasisMetadata = _authorizationBasisMetadataSelector.SelectAuthorizationBasisMetadata(
apiClientContext.ClaimSetName,
resourceClaimUris,
actionUri);
dataManagementRequestContext.ApiClientContext.ClaimSetName,
dataManagementRequestContext.ResourceClaimUris,
dataManagementRequestContext.Action);

var relevantClaims = new[] { authorizationBasisMetadata.RelevantClaim };

// Get authorization filters
var authorizationFiltering = authorizationBasisMetadata.AuthorizationStrategies
.Distinct()
.Select(x => x.GetAuthorizationStrategyFiltering(relevantClaims, dataManagementRequestContext))
Expand All @@ -80,6 +93,37 @@ public DataManagementAuthorizationPlan CreateAuthorizationPlan(Resource resource
Filtering = authorizationFiltering
};
}

private DataManagementRequestContext CreateDataManagementRequestContext(Resource resource, string actionUri)
{
string[] resourceClaimUris = _resourceClaimUriProvider.GetResourceClaimUris(resource);
var apiClientContext = _apiClientContextProvider.GetApiClientContext();

return new DataManagementRequestContext(
apiClientContext,
resource,
resourceClaimUris,
actionUri,
(Type)(resource.Entity as dynamic).NHibernateEntityType);
}

private DataManagementRequestContext CreateDataManagementRequestContext(
Resource resource,
string actionUri,
object entity,
AuthorizationPhase authorizationPhase)
{
string[] resourceClaimUris = _resourceClaimUriProvider.GetResourceClaimUris(resource);
var apiClientContext = _apiClientContextProvider.GetApiClientContext();

return new DataManagementRequestContext(
apiClientContext,
resource,
resourceClaimUris,
actionUri,
entity,
authorizationPhase);
}
}

public class DataManagementAuthorizationPlan
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,29 @@ public interface IDataManagementAuthorizationPlanFactory
DataManagementAuthorizationPlan CreateAuthorizationPlan(string actionUri);

/// <summary>
/// Authorizes a multiple-item read request using the specified resource and action (supplemented with other pertinent information available in the call context).
/// Authorizes a single-item request using the specified action, entity instance and authorization phase for the resource
/// identified in the <see cref="DataManagementRequestContext"/> (and supplemented with other pertinent information available
/// in the call context).
/// </summary>
/// <param name="actionUri">The URI representation of the action being performed by the current request.</param>
/// <param name="entity">The entity instance containing the data to be authorized.</param>
/// <param name="authorizationPhase">An indication of whether this entity represents the proposed (new) or existing data.</param>
/// <returns>A plan containing relevant context for authorization and the filters to be applied to the query.</returns>
DataManagementAuthorizationPlan CreateAuthorizationPlan(
string actionUri,
object entity,
AuthorizationPhase authorizationPhase);

/// <summary>
/// Authorizes the inclusion of the specified resource (e.g. such as while processing a composite response) and action
/// (supplemented with other pertinent information available in the call context).
/// </summary>
/// <param name="resource">The <see cref="Resource" /> on which authorization is being performed. If null, the data management contextual resource will be used.</param>
/// <param name="actionUri">The URI representation of the action being performed by the current request.</param>
/// <returns>A plan containing relevant context for authorization and the filters to be applied to the query.</returns>
/// <remarks>This overload was added specifically to provide for authorization of "child" resources of a composite definition
/// while processing the queries for obtaining the data for the composite response. In this case, the contextual resource is
/// not relevant as this will be driven by the composite definition.
/// </remarks>
DataManagementAuthorizationPlan CreateAuthorizationPlan(Resource resource, string actionUri);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using EdFi.Ods.Common.Models;
using EdFi.Ods.Common.Models.Domain.DomainModelEnhancers;
using EdFi.Ods.Common.Models.Resource;
using EdFi.Ods.Common.Security.Claims;
using EdFi.Ods.Features.ChangeQueries.Repositories.DeletedItems;
using EdFi.Security.DataAccess.Repositories;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using EdFi.Ods.Common.Models;
using EdFi.Ods.Common.Models.Domain.DomainModelEnhancers;
using EdFi.Ods.Common.Models.Resource;
using EdFi.Ods.Common.Security.Claims;
using EdFi.Ods.Features.ChangeQueries.Repositories.KeyChanges;
using EdFi.Security.DataAccess.Repositories;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@
using System.Security;
using EdFi.Ods.Api.Security.Authorization.Filtering;
using EdFi.Ods.Api.Security.AuthorizationStrategies.Relationships.Filters;
using EdFi.Ods.Common.Constants;
using EdFi.Ods.Common.Database.Querying;
using EdFi.Ods.Common.Infrastructure.Filtering;
using EdFi.Ods.Common.Models;
using EdFi.Ods.Common.Models.Domain.DomainModelEnhancers;
using EdFi.Ods.Common.Models.Resource;
using EdFi.Ods.Common.Security.Authorization;
using EdFi.Ods.Common.Security.Claims;
using EdFi.Security.DataAccess.Repositories;

namespace EdFi.Ods.Features.ChangeQueries.Repositories.Authorization
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,20 @@ public async Task AuthorizeEntityAsync_ShouldCallCreateAuthorizationPlan()
// Arrange
var actionUri = "some-action-uri";
var entity = new object();
var authorizationPlan = new DataManagementAuthorizationPlan();
var authorizationPhase = AuthorizationPhase.ExistingData;

var authorizationPlan = new DataManagementAuthorizationPlan();
authorizationPlan.RequestContext = CreateAuthorizationPlanRequestContext(actionUri, entity);
authorizationPlan.AuthorizationBasisMetadata = new AuthorizationBasisMetadata(null, null, "validation-rule-set");

A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri))
A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri, entity, authorizationPhase))
.Returns(authorizationPlan);

// Act
await _entityAuthorizer.AuthorizeEntityAsync(entity, actionUri, AuthorizationPhase.ExistingData, CancellationToken.None);

// Assert
A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri))
A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri, entity, authorizationPhase))
.MustHaveHappenedOnceExactly();
}

Expand All @@ -93,13 +94,13 @@ public async Task AuthorizeEntityAsync_ShouldCallValidateOnAuthorizationValidato
// Arrange
var actionUri = "some-action-uri";
var entity = new object();
var authorizationPlan = new DataManagementAuthorizationPlan();
var authorizationPhase = AuthorizationPhase.ExistingData;

var authorizationPlan = new DataManagementAuthorizationPlan();
authorizationPlan.RequestContext = CreateAuthorizationPlanRequestContext(actionUri, entity);
authorizationPlan.AuthorizationBasisMetadata = new AuthorizationBasisMetadata(null, null, "validation-rule-set");

A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri))
A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri, entity, authorizationPhase))
.Returns(authorizationPlan);

// Act
Expand All @@ -119,8 +120,9 @@ public async Task AuthorizeEntityAsync_ShouldThrowException_WhenDelegateFilterAu
// Arrange
var actionUri = "some-action-uri";
var entity = new object();
var authorizationPlan = new DataManagementAuthorizationPlan();
var authorizationPhase = AuthorizationPhase.ExistingData;

var authorizationPlan = new DataManagementAuthorizationPlan();
authorizationPlan.RequestContext = CreateAuthorizationPlanRequestContext(actionUri, entity);
authorizationPlan.AuthorizationBasisMetadata = new AuthorizationBasisMetadata(null, null, "validation-rule-set");

Expand All @@ -137,7 +139,7 @@ public async Task AuthorizeEntityAsync_ShouldThrowException_WhenDelegateFilterAu

authorizationPlan.Filtering = new[] { A.Fake<AuthorizationStrategyFiltering>() };

A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri))
A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri, entity, authorizationPhase))
.Returns(authorizationPlan);

A.CallTo(() => _delegateFilterAuthorizer.PerformInstanceBasedAuthorization(FilterOperator.And, authorizationPlan.Filtering, authorizationPlan.RequestContext))
Expand All @@ -156,7 +158,8 @@ public async Task AuthorizeEntityAsync_ShouldPerformViewBasedAuthorizationAsync_
// Arrange
var actionUri = "some-action-uri";
var entity = new object();

var authorizationPhase = AuthorizationPhase.ExistingData;

var authorizationPlan = new DataManagementAuthorizationPlan();
authorizationPlan.RequestContext = CreateAuthorizationPlanRequestContext(actionUri, entity);
authorizationPlan.AuthorizationBasisMetadata = new AuthorizationBasisMetadata(null, null, "validation-rule-set");
Expand All @@ -174,7 +177,7 @@ public async Task AuthorizeEntityAsync_ShouldPerformViewBasedAuthorizationAsync_

authorizationPlan.Filtering = new[] { new AuthorizationStrategyFiltering() };

A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri))
A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri, entity, authorizationPhase))
.Returns(authorizationPlan);

A.CallTo(() => _delegateFilterAuthorizer.PerformInstanceBasedAuthorization(FilterOperator.And, authorizationPlan.Filtering, authorizationPlan.RequestContext))
Expand All @@ -197,6 +200,8 @@ public async Task AuthorizeEntityAsync_ShouldAuthorize_WhenOrConditionSatisfied(
// Arrange
var actionUri = "some-action-uri";
var entity = new object();
var authorizationPhase = AuthorizationPhase.ExistingData;

var authorizationPlan = new DataManagementAuthorizationPlan();
authorizationPlan.RequestContext = CreateAuthorizationPlanRequestContext(actionUri, entity);
authorizationPlan.AuthorizationBasisMetadata = new AuthorizationBasisMetadata(null, null, "validation-rule-set");
Expand All @@ -215,14 +220,14 @@ public async Task AuthorizeEntityAsync_ShouldAuthorize_WhenOrConditionSatisfied(
authorizationPlan.Filtering = new[] { A.Fake<AuthorizationStrategyFiltering>() };
authorizationPlan.RequestContext = A.Fake<DataManagementRequestContext>();

A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri))
A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri, entity, authorizationPhase))
.Returns(authorizationPlan);

A.CallTo(() => _delegateFilterAuthorizer.PerformInstanceBasedAuthorization(FilterOperator.Or, authorizationPlan.Filtering, authorizationPlan.RequestContext))
.Returns(new[] { orConditionResult });

// Act
await _entityAuthorizer.AuthorizeEntityAsync(entity, actionUri, AuthorizationPhase.ExistingData, CancellationToken.None);
await _entityAuthorizer.AuthorizeEntityAsync(entity, actionUri, authorizationPhase, CancellationToken.None);

// Assert
A.CallTo(() => _viewBasedFilterAuthorizer.PerformViewBasedAuthorizationAsync(A<AuthorizationStrategyFilterResults[]>.Ignored,
Expand All @@ -237,7 +242,8 @@ public async Task AuthorizeEntityAsync_ShouldPerformViewBasedAuthorization_WhenP
// Arrange
var actionUri = "some-action-uri";
var entity = new object();

var authorizationPhase = AuthorizationPhase.ExistingData;

var authorizationPlan = new DataManagementAuthorizationPlan();
authorizationPlan.RequestContext = CreateAuthorizationPlanRequestContext(actionUri, entity);
authorizationPlan.AuthorizationBasisMetadata = new AuthorizationBasisMetadata(null, null, "validation-rule-set");
Expand Down Expand Up @@ -268,7 +274,7 @@ public async Task AuthorizeEntityAsync_ShouldPerformViewBasedAuthorization_WhenP
};


A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri))
A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri, entity, authorizationPhase))
.Returns(authorizationPlan);

A.CallTo(() => _delegateFilterAuthorizer.PerformInstanceBasedAuthorization(FilterOperator.And, authorizationPlan.Filtering, authorizationPlan.RequestContext))
Expand All @@ -294,6 +300,7 @@ public async Task AuthorizeEntityAsync_ShouldThrow_WhenAllPendingExistenceChecks
// Arrange
var actionUri = "some-action-uri";
var entity = new object();
var authorizationPhase = AuthorizationPhase.ExistingData;

var authorizationPlan = new DataManagementAuthorizationPlan();
authorizationPlan.RequestContext = CreateAuthorizationPlanRequestContext(actionUri, entity);
Expand All @@ -311,7 +318,7 @@ public async Task AuthorizeEntityAsync_ShouldThrow_WhenAllPendingExistenceChecks
}
};

A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri))
A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri, entity, authorizationPhase))
.Returns(authorizationPlan);

A.CallTo(() => _delegateFilterAuthorizer.PerformInstanceBasedAuthorization(FilterOperator.And, authorizationPlan.Filtering, authorizationPlan.RequestContext))
Expand All @@ -333,6 +340,7 @@ public async Task AuthorizeEntityAsync_ShouldThrow_WhenPendingOrStrategyResultsI
// Arrange
var actionUri = "some-action-uri";
var entity = new object();
var authorizationPhase = AuthorizationPhase.ExistingData;

var authorizationPlan = new DataManagementAuthorizationPlan();
authorizationPlan.RequestContext = CreateAuthorizationPlanRequestContext(actionUri, entity);
Expand Down Expand Up @@ -361,7 +369,7 @@ public async Task AuthorizeEntityAsync_ShouldThrow_WhenPendingOrStrategyResultsI
}
};

A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri))
A.CallTo(() => _authorizationPlanFactory.CreateAuthorizationPlan(actionUri, entity, authorizationPhase))
.Returns(authorizationPlan);

A.CallTo(() => _delegateFilterAuthorizer.PerformInstanceBasedAuthorization(FilterOperator.And, authorizationPlan.Filtering, authorizationPlan.RequestContext))
Expand Down

0 comments on commit 5010905

Please sign in to comment.