Example #1
0
        /// <summary>
        /// Applies processing related to the usage/entry to another top-level resource (e.g. applying authorization concerns).
        /// </summary>
        /// <param name="processorContext">The composite definition processor context.</param>
        /// <param name="builderContext">The current builder context.</param>
        /// <returns><b>true</b> if the resource can be processed; otherwise <b>false</b>.</returns>
        public bool TryIncludeResource(CompositeDefinitionProcessorContext processorContext, HqlBuilderContext builderContext)
        {
            var resourceClass = processorContext.CurrentResourceClass;

            if (!(resourceClass is Resource))
            {
                throw new InvalidOperationException(
                          $"Unable to evaluate resource '{resourceClass.FullName}' for inclusion in HQL query because it is not the root class of the resource.");
            }

            var resource = (Resource)resourceClass;

            // --------------------------
            //   Determine inclusion
            // --------------------------
            var entityType = GetEntityType(resource);

            var authorizationContext = new EdFiAuthorizationContext(
                ClaimsPrincipal.Current,
                _resourceClaimUriProvider.GetResourceClaimUris(resource),
                RequestActions.ReadActionUri,
                entityType);

            // Authorize and apply filtering
            IReadOnlyList <AuthorizationFilterDetails> authorizationFilters;

            try
            {
                // NOTE: Possible performance optimization - Allow for "Try" semantics (so no exceptions are thrown here)
                authorizationFilters = _authorizationProvider.GetAuthorizationFilters(authorizationContext);
            }
            catch (EdFiSecurityException ex)
            {
                // If this is the base resource, rethrow the exception to achieve a 401 response
                if (processorContext.IsBaseResource())
                {
                    Logger.Debug($"BaseResource: {processorContext.CurrentResourceClass.Name} could not be authorized.");
                    throw;
                }

                // In the case where we have an abstract class and it has no claim, eg EducationOrganization, we will allow
                // the join if the subtype has been included.
                if (processorContext.IsAbstract())
                {
                    Logger.Debug($"Resource {processorContext.CurrentResourceClass.Name} has no claim.");

                    if (processorContext.ShouldIncludeResourceSubtype())
                    {
                        Logger.Debug(
                            $"Resource is abstract and so target resource '{processorContext.CurrentResourceClass.Name}' cannot be authorized. Join will be included, but non-identifying resource members should be stripped from results.");

                        return(true);
                    }
                }

                Logger.Debug($"Resource {processorContext.CurrentResourceClass.Name} is excluded from the request.");
                Logger.Debug($"Security Exception Message: {ex.Message}.");

                return(false);
            }

            // Save the filters to be applied to this query for use later in the process
            builderContext.CurrentQueryFilterByName = authorizationFilters.ToDictionary(x => x.FilterName, x => x);

            return(true);
        }
        private TBuildResult ProcessDefinition(
            TBuildResult parentResult,
            TBuilderContext builderContext,
            CompositeDefinitionProcessorContext processorContext)
        {
            // Apply authorization for aggregate roots here
            if (processorContext.CurrentResourceClass.Entity.IsAggregateRoot)
            {
                // Provide opportunity to perform processing related to navigating into another resource (i.e. authorization)
                if (!_compositeBuilder.TryIncludeResource(processorContext, builderContext))
                {
                    return(default(TBuildResult));
                }
            }

            bool isMainQuery = processorContext.IsBaseResource();

            if (isMainQuery)
            {
                _compositeBuilder.ApplyRootResource(processorContext, builderContext);
            }
            else
            {
                _compositeBuilder.ApplyChildResource(builderContext, processorContext);
            }

            var nonIncomingIdentifyingProperties = processorContext.NonIncomingIdentifyingProperties();

            ApplyLocalIdentifyingProperties(builderContext, processorContext, nonIncomingIdentifyingProperties);

            // Capture current applicable builder state so it can be modified further at this level without changes affecting children
            _compositeBuilder.SnapshotParentingContext(builderContext);

            // Select projected properties
            var propertyProjections = GetPropertyProjections(processorContext, processorContext.PropertyElements());

            // Project the properties into the artifact under construction
            _compositeBuilder.ProjectProperties(propertyProjections, builderContext, processorContext);

            // Process flattened References
            ProcessFlattenedMemberProperties(processorContext, builderContext);

            TBuildResult thisBuildResult;

            if (isMainQuery)
            {
                // Short circuit the rest of the processing if the root result is null
                if (!_compositeBuilder.TryBuildForRootResource(builderContext, processorContext, out thisBuildResult))
                {
                    return(default(TBuildResult));
                }
            }
            else
            {
                thisBuildResult = _compositeBuilder.BuildForChildResource(
                    parentResult,
                    builderContext,
                    processorContext);
            }

            var childBuilderContext = _compositeBuilder.CreateParentingContext(builderContext);

            ProcessChildren(thisBuildResult, processorContext, childBuilderContext);

            if (_performValidation && _validationErrors.Any())
            {
                throw new Exception(string.Join(Environment.NewLine, _validationErrors.ToArray()));
            }

            return(thisBuildResult);
        }