Esempio n. 1
0
        public void TestClaimCheckResponse()
        {
            byte[] input    = File.ReadAllBytes("ClaimCheckResponse.xml");
            var    response = new ClaimCheckResponse(input);

            Assert.AreEqual("1000", response.Code);
            Assert.AreEqual("Command completed successfully", response.Message);

            Assert.AreEqual(2, response.Results.Count);

            Assert.AreEqual("example1.com", response.Results[0].Name);
            Assert.AreEqual(true, response.Results[0].ClaimExists);
            Assert.AreEqual("2016100401/2/D/7/LXsvtbnP8C5bUdxDuoj-XvJK0000000263", response.Results[0].ClaimKeys[0].Value);


            Assert.AreEqual("example2.com", response.Results[1].Name);
            Assert.AreEqual(false, response.Results[1].ClaimExists);
            Assert.AreEqual(0, response.Results[1].ClaimKeys.Count);
        }
        private ClaimCheckResponse GetRelevantPrincipalClaims(
            IList <string> authorizingClaimNames,
            string requestedAction,
            ClaimsPrincipal principal)
        {
            var response = new ClaimCheckResponse();

            // Find matching claims, while preserving the ordering of the incoming claim names
            var principalClaimsToEvaluate =
                (from cn in authorizingClaimNames
                 join pc in principal.Claims on cn equals pc.Type
                 select pc)
                .ToList();

            // 1) First check: Lets make sure the principal at least has a claim that applies for this resource.
            if (!principalClaimsToEvaluate.Any())
            {
                response.Success = false;

                var apiClientResourceClaims = principal.Claims.Select(c => c.Type)
                                              .Where(x => x.StartsWith(EdFi.Ods.Common.Conventions.EdFiConventions.EdFiOdsResourceClaimBaseUri));

                string apiClientClaimSetName =
                    principal.Claims.SingleOrDefault(c => c.Type == EdFiOdsApiClaimTypes.ClaimSetName)?.Value;

                response.SecurityExceptionMessage =
                    $@"Access to the resource could not be authorized. Are you missing a claim? This resource can be authorized by the following claims:
    {string.Join(Environment.NewLine + "    ", authorizingClaimNames)}
The API client has been assigned the '{apiClientClaimSetName}' claim set with the following resource claims:
    {string.Join(Environment.NewLine + "    ", apiClientResourceClaims)}";

                return(response);
            }

            // 2) Second check: Of the claims that apply for this resource do we have any that match the action requested or a higher action?
            var edfiClaimValuesToEvaluate = principalClaimsToEvaluate.Select(
                x => new
            {
                Claim = x, EdFiResourceClaimValue = x.ToEdFiResourceClaimValue()
            });

            var claimsWithMatchingActions = edfiClaimValuesToEvaluate.Where(
                x => IsRequestedActionSatisfiedByClaimActions(
                    requestedAction,
                    x.EdFiResourceClaimValue.Actions.Select(y => y.Name)))
                                            .ToList();

            if (!claimsWithMatchingActions.Any())
            {
                response.Success = false;

                response.SecurityExceptionMessage = string.Format(
                    "Access to the resource could not be authorized for the requested action '{0}'.",
                    requestedAction);

                return(response);
            }

            response.Success = true;

            response.RelevantClaims = claimsWithMatchingActions.Select(x => x.Claim)
                                      .ToList();

            return(response);
        }
    private ClaimCheckResponse PerformClaimCheck(EdFiAuthorizationContext authorizationContext)
    {
        // Validate the context
        ValidateAuthorizationContext(authorizationContext);

        // Extract individual values
        string[] requestedResourceClaimUris = authorizationContext.Resource.Select(x => x.Value).ToArray();

        string requestedAction = authorizationContext.Action.Single().Value;

        // NOTE: GKM - Review all use of the ClaimsPrincipal, and consider eliminating it for CallContext
        var principal = authorizationContext.Principal;

        // Obtain the resource claim/authorization strategy pairs that could be used for authorizing this particular request
        var resourceClaimAuthorizationMetadata = GetResourceClaimAuthorizationMetadatas(requestedResourceClaimUris, requestedAction);

        // Get the names of the resource claims that could be used for authorization
        var authorizingResourceClaimNames = resourceClaimAuthorizationMetadata
                                            .Select(x => x.ClaimName)
                                            .ToList();

        // Intersect the potentially authorizing resource claims against the principal's claims
        var claimCheckResponse = GetRelevantPrincipalClaims(authorizingResourceClaimNames, requestedAction, principal);

        if (claimCheckResponse.Success)
        {
            claimCheckResponse.RequestedAction       = requestedAction;
            claimCheckResponse.RequestedResourceUris = requestedResourceClaimUris;
            claimCheckResponse.AuthorizationMetadata = resourceClaimAuthorizationMetadata;
        }

        return(claimCheckResponse);

        void ValidateAuthorizationContext(EdFiAuthorizationContext authorizationContext)
        {
            if (authorizationContext == null)
            {
                throw new ArgumentNullException("authorizationContext");
            }

            if (authorizationContext.Resource == null || authorizationContext.Resource.All(r => string.IsNullOrWhiteSpace(r.Value)))
            {
                throw new AuthorizationContextException("Authorization can only be performed if a resource is specified.");
            }

            if (authorizationContext.Resource.Count > 2)
            {
                throw new AuthorizationContextException($"Unexpected number of Resource URIs found in the authorization context. Expected up to 2, but found {authorizationContext.Resource.Count}.");
            }

            if (authorizationContext.Action == null || authorizationContext.Action.All(a => string.IsNullOrWhiteSpace(a.Value)))
            {
                throw new AuthorizationContextException("Authorization can only be performed if an action is specified.");
            }

            if (authorizationContext.Action.Count > 1)
            {
                throw new AuthorizationContextException("Authorization can only be performed on requests with a single action.");
            }
        }

        List <ResourceClaimAuthorizationMetadata> GetResourceClaimAuthorizationMetadatas(
            string[] requestedResourceClaimUris,
            string requestedAction)
        {
            // Processing the URIs in order of priority, return the first one found with associated
            // authorization metadata
            var resourceClaimAuthorizationMetadata =
                requestedResourceClaimUris
                .Select(
                    requestedResourceClaimUri => _resourceAuthorizationMetadataProvider
                    .GetResourceClaimAuthorizationMetadata(requestedResourceClaimUri, requestedAction)
                    .ToList())
                .FirstOrDefault(x => x.Any());

            // Return the authorization metadata located, or an empty list.
            return(resourceClaimAuthorizationMetadata
                   ?? new List <ResourceClaimAuthorizationMetadata>());
        }

        ClaimCheckResponse GetRelevantPrincipalClaims(
            IList <string> authorizingClaimNames,
            string requestedAction,
            ClaimsPrincipal principal)
        {
            var response = new ClaimCheckResponse();

            // Find matching claims, while preserving the ordering of the incoming claim names
            var principalClaimsToEvaluate = (from cn in authorizingClaimNames
                                             join pc in principal.Claims on cn equals pc.Type
                                             select pc).ToList();

            // 1) First check: Lets make sure the principal at least has a claim that applies for this resource.
            if (!principalClaimsToEvaluate.Any())
            {
                response.Success = false;

                var apiClientResourceClaims = principal.Claims.Select(c => c.Type)
                                              .Where(x => x.StartsWith(EdFiConventions.EdFiOdsResourceClaimBaseUri));

                string apiClientClaimSetName =
                    principal.Claims.SingleOrDefault(c => c.Type == EdFiOdsApiClaimTypes.ClaimSetName)?.Value;

                response.SecurityExceptionMessage =
                    $@"Access to the resource could not be authorized. Are you missing a claim? This resource can be authorized by the following claims:
    {string.Join(Environment.NewLine + "    ", authorizingClaimNames)}
The API client has been assigned the '{apiClientClaimSetName}' claim set with the following resource claims:
    {string.Join(Environment.NewLine + "    ", apiClientResourceClaims)}";

                return(response);
            }

            // 2) Second check: Of the claims that apply for this resource do we have any that match the action requested or a higher action?
            var edfiClaimValuesToEvaluate = principalClaimsToEvaluate.Select(
                x => new
            {
                Claim = x,
                EdFiResourceClaimValue = x.ToEdFiResourceClaimValue()
            });

            var claimsWithMatchingActions = edfiClaimValuesToEvaluate.Where(
                x => IsRequestedActionSatisfiedByClaimActions(
                    requestedAction,
                    x.EdFiResourceClaimValue.Actions.Select(y => y.Name)))
                                            .ToList();

            if (!claimsWithMatchingActions.Any())
            {
                response.Success = false;

                response.SecurityExceptionMessage = string.Format(
                    "Access to the resource could not be authorized for the requested action '{0}'.",
                    requestedAction);

                return(response);
            }

            response.Success = true;

            response.RelevantClaims = claimsWithMatchingActions.Select(x => x.Claim).ToList();

            return(response);
        }

        bool IsRequestedActionSatisfiedByClaimActions(string requestedAction, IEnumerable <string> principalClaimActions)
        {
            int requestedActionValue = GetBitValuesByAction(requestedAction);

            // Determine if any of the claim actions can satisfy the requested action
            return(principalClaimActions.Any(x => (requestedActionValue & GetBitValuesByAction(x)) != 0));
        }

        int GetBitValuesByAction(string action)
        {
            int result;

            if (_bitValuesByAction.Value.TryGetValue(action, out result))
            {
                return(result);
            }

            // Should never happen
            throw new NotSupportedException("The requested action is not a supported action.  Authorization cannot be performed.");
        }
    }