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);
        }