/// <summary> /// Invokes authorization of the request using the resource currently in context but wit /// an override action (e.g. for converting the "Upsert" action to either "Create" or "Update"). /// </summary> /// <param name="entity">The request/entity being authorized.</param> /// <param name="actionUri">The action being performed with the request/entity.</param> protected async Task AuthorizeSingleItemAsync(T entity, string actionUri, CancellationToken cancellationToken) { // Make sure Authorization context is present before proceeding _authorizationContextProvider.VerifyAuthorizationContextExists(); // Build the AuthorizationContext EdFiAuthorizationContext authorizationContext = new EdFiAuthorizationContext( ClaimsPrincipal.Current, _authorizationContextProvider.GetResourceUris(), actionUri, entity); // Authorize the call await _authorizationProvider.AuthorizeSingleItemAsync(authorizationContext, cancellationToken); }
/// <summary> /// Invokes authorization of the request using the resource currently in context but wit /// an override action (e.g. for converting the "Upsert" action to either "Create" or "Update"). /// </summary> /// <param name="entity">The request/entity being authorized.</param> /// <param name="actionUri">The action being performed with the request/entity.</param> /// <param name="cancellationToken"></param> protected async Task AuthorizeSingleItemAsync(TEntity entity, string actionUri, CancellationToken cancellationToken) { // Make sure Authorization context is present before proceeding _authorizationContextProvider.VerifyAuthorizationContextExists(); // Build the AuthorizationContext var authorizationContext = new EdFiAuthorizationContext( _apiKeyContextProvider.GetApiKeyContext(), ClaimsPrincipal.Current, _authorizationContextProvider.GetResourceUris(), actionUri, entity); var authorizationBasisMetadata = _authorizationBasisMetadataSelector.SelectAuthorizationBasisMetadata(authorizationContext); ExecuteAuthorizationValidationRules(authorizationContext, authorizationBasisMetadata); // Get the authorization filtering information var authorizationFiltering = _authorizationFilteringProvider.GetAuthorizationFiltering(authorizationContext, authorizationBasisMetadata); var andResults = PerformInstanceBasedAuthorization(authorizationFiltering, authorizationContext, FilterOperator.And); // If any failures occurred with the AND strategies, throw the first exception now ThrowInstanceBasedFailureFromResults(andResults); // For remaining pending authorizations requiring database access, get the existence checks SQL fragments var pendingAndStrategies = andResults // Only check any strategies that have no failures .Where(x => x.FilterResults.Any(f => f.Result.State == AuthorizationState.NotPerformed)) .Select(x => new AuthorizationStrategyFilterResults { AuthorizationStrategyName = x.AuthorizationStrategyName, Operator = x.Operator, FilterResults = x.FilterResults .Where(y => y.Result.State == AuthorizationState.NotPerformed) .ToArray(), }) .ToArray(); var orResults = PerformInstanceBasedAuthorization(authorizationFiltering, authorizationContext, FilterOperator.Or); bool orConditionAlreadySatisfied = orResults .Any(r => r.FilterResults.All(f => f.Result.State == AuthorizationState.Success)); if (orConditionAlreadySatisfied || !orResults.Any()) { // Check for pending ANDs if (pendingAndStrategies.Any()) { // Execute SQL to determine AND results await PerformViewBasedAuthorizationAsync(pendingAndStrategies, authorizationContext, cancellationToken); } // We're authorized... return; } // We'll need to go to the database to check for relationship existence var pendingOrStrategies = orResults // Only check any strategies that have no failures .Where(x => x.FilterResults.All(f => f.Result.State != AuthorizationState.Failed)) .Select( x => new AuthorizationStrategyFilterResults { AuthorizationStrategyName = x.AuthorizationStrategyName, Operator = x.Operator, FilterResults = x.FilterResults .Where(y => y.Result.State == AuthorizationState.NotPerformed) .ToArray(), }); var allPendingExistenceChecks = pendingAndStrategies.Where(x => x.FilterResults.Any()) .Concat(pendingOrStrategies.Where(x => x.FilterResults.Any())) .ToArray(); // If there are no pending view-based checks to be performed and we're still here, the authorization failure is held in the orResults if (!allPendingExistenceChecks.Any()) { ThrowInstanceBasedFailureFromResults(orResults); } await PerformViewBasedAuthorizationAsync(allPendingExistenceChecks, authorizationContext, cancellationToken); bool IsCreateUpdateOrDelete(EdFiAuthorizationContext authorizationContext) { return((_bitValuesByAction.Value[authorizationContext.Action.Single().Value] & (Actions.Create | Actions.Update | Actions.Delete)) != 0); } void ExecuteAuthorizationValidationRules( EdFiAuthorizationContext edFiAuthorizationContext, AuthorizationBasisMetadata authorizationBasisMetadata1) { // If there are explicit object validators, and we're modifying data if (_explicitObjectValidators.Any() && IsCreateUpdateOrDelete(edFiAuthorizationContext)) { // Validate the object using explicit validation var validationResults = _explicitObjectValidators.ValidateObject( edFiAuthorizationContext.Data, authorizationBasisMetadata1.ValidationRuleSetName); if (!validationResults.IsValid()) { throw new ValidationException( string.Format( "Validation of '{0}' failed.\n{1}", edFiAuthorizationContext.Data.GetType().Name, string.Join("\n", validationResults.GetAllMessages(indentLevel: 1)))); } } } AuthorizationStrategyFilterResults[] PerformInstanceBasedAuthorization( IReadOnlyList <AuthorizationStrategyFiltering> authorizationStrategyFilterings, EdFiAuthorizationContext authorizationContext1, FilterOperator filterOperator) { var andResults = authorizationStrategyFilterings .Where(asf => asf.Operator == filterOperator) .Select( s => new AuthorizationStrategyFilterResults { AuthorizationStrategyName = s.AuthorizationStrategyName, Operator = s.Operator, FilterResults = s.Filters .Select( f => new { FilterDefinition = _authorizationFilterDefinitionProvider.GetFilterDefinition(f.FilterName), FilterContext = f }) .Select( x => new FilterAuthorizationResult { FilterDefinition = x.FilterDefinition, FilterContext = x.FilterContext, Result = x.FilterDefinition.AuthorizeInstance(authorizationContext1, x.FilterContext) }) .ToArray() }) .ToArray(); return(andResults); } void ThrowInstanceBasedFailureFromResults(AuthorizationStrategyFilterResults[] results) { results.SelectMany(x => x.FilterResults) .Where(fr => fr.Result.State == AuthorizationState.Failed) .ForEach(fr => throw fr.Result.Exception); } }