コード例 #1
0
        /// <summary>
        /// Called by the runtime to invoke the middleware logic. In most cases, the middleware component should return the task
        /// generated by calling the next delegate in the request chain with the provided context.
        /// </summary>
        /// <param name="context">The invocation request governing data in this pipeline run.</param>
        /// <param name="next">The next delegate in the chain to be invoked.</param>
        /// <param name="cancelToken">The cancel token.</param>
        /// <returns>Task.</returns>
        public async Task InvokeAsync(GraphFieldExecutionContext context, GraphMiddlewareInvocationDelegate <GraphFieldExecutionContext> next, CancellationToken cancelToken)
        {
            FieldAuthorizationResult result = FieldAuthorizationResult.Default();

            if (context.IsValid)
            {
                // execute the authorization pipeline
                var authRequest = new GraphFieldAuthorizationRequest(context.Request);
                var authContext = new GraphFieldAuthorizationContext(context, authRequest);
                await _authPipeline.InvokeAsync(authContext, cancelToken).ConfigureAwait(false);

                result = authContext.Result ?? FieldAuthorizationResult.Default();

                // by default, deny any stati not explicitly declared as "successful" by this component.
                if (!result.Status.IsAuthorized())
                {
                    context.Messages.Critical(
                        $"Access Denied to field {context.Field.Route.Path}",
                        Constants.ErrorCodes.ACCESS_DENIED,
                        context.Request.Origin);
                }
            }

            if (!result.Status.IsAuthorized())
            {
                context.ResolvedSourceItems.AddRange(context.Request.DataSource.Items);
                context.ResolvedSourceItems.ForEach(x => x.Fail());
            }

            await next(context, cancelToken).ConfigureAwait(false);
        }
コード例 #2
0
        /// <summary>
        /// Invokes this middleware component allowing it to perform its work against the supplied context.
        /// </summary>
        /// <param name="context">The context containing the request passed through the pipeline.</param>
        /// <param name="next">The delegate pointing to the next piece of middleware to be invoked.</param>
        /// <param name="cancelToken">The cancel token.</param>
        /// <returns>Task.</returns>
        public async Task InvokeAsync(GraphFieldAuthorizationContext context, GraphMiddlewareInvocationDelegate <GraphFieldAuthorizationContext> next, CancellationToken cancelToken)
        {
            context.Logger?.FieldResolutionSecurityChallenge(context);

            var result = await this.AuthorizeRequest(context.Request, context.User).ConfigureAwait(false);

            context.Result = result ?? FieldAuthorizationResult.Default();

            context.Logger?.FieldResolutionSecurityChallengeResult(context);

            await next(context, cancelToken).ConfigureAwait(false);
        }
コード例 #3
0
        public void FieldSecurityChallengeCompletedLogEntry()
        {
            var builder = new TestServerBuilder()
                          .AddGraphType <LogTestController>();

            builder.User.SetUsername("bobSmith");
            var server = builder.Build();

            var package      = server.CreateFieldContextBuilder <LogTestController>(nameof(LogTestController.ExecuteField2));
            var fieldRequest = package.FieldRequest;
            var authContext  = package.CreateAuthorizationContext();

            authContext.Result = FieldAuthorizationResult.Fail("test message 1");
            var entry = new FieldAuthorizationCompletedLogEntry(authContext);

            Assert.AreEqual(LogEventIds.FieldAuthorizationCompleted.Id, entry.EventId);
            Assert.AreEqual(fieldRequest.Id, entry.PipelineRequestId);
            Assert.AreEqual(fieldRequest.Field.Route.Path, entry.FieldPath);
            Assert.AreEqual(authContext.User?.RetrieveUsername(), entry.Username);
            Assert.AreEqual(authContext.Result.Status.ToString(), entry.AuthorizationStatus);
            Assert.IsNotNull(entry.ToString());
            Assert.AreEqual(authContext.Result.LogMessage, entry.LogMessage);
        }
コード例 #4
0
        /// <summary>
        /// Iterates over every secure field in the operation on the context, attempting to authorize the
        /// user to each one.
        /// </summary>
        /// <param name="context">The primary query context.</param>
        /// <param name="cancelToken">The cancel token.</param>
        /// <returns><c>true</c> if authorization was successful, otherwise false.</returns>
        private async Task <bool> AuthorizeOperation(GraphQueryExecutionContext context, CancellationToken cancelToken)
        {
            var  authTasks      = new List <Task>();
            bool anyFieldFailed = false;

            foreach (var fieldContext in context.QueryOperation.SecureFieldContexts)
            {
                var authRequest = new GraphFieldAuthorizationRequest(fieldContext);
                var authContext = new GraphFieldAuthorizationContext(context, authRequest);

                var pipelineTask = _authPipeline.InvokeAsync(authContext, cancelToken)
                                   .ContinueWith(
                    (_) =>
                {
                    var authResult = authContext.Result ?? FieldAuthorizationResult.Default();

                    // fake the path elements from the field route. since we don't have a full resolution chain
                    // when doing query level authorization (no indexers on potential child fields since
                    // nothing is actually resolved yet)
                    if (!authResult.Status.IsAuthorized())
                    {
                        context.Messages.Critical(
                            $"Access Denied to field {fieldContext.Field.Route.Path}",
                            Constants.ErrorCodes.ACCESS_DENIED,
                            fieldContext.Origin);
                        anyFieldFailed = true;
                    }
                }, cancelToken);

                authTasks.Add(pipelineTask);
            }

            await Task.WhenAll(authTasks).ConfigureAwait(false);

            return(anyFieldFailed);
        }
コード例 #5
0
        /// <summary>
        /// Attempts to authorize the request using the field based <see cref="IAuthorizeData" /> attributes.
        /// </summary>
        /// <param name="authRequest">The request to authorize.</param>
        /// <param name="claimsUser">The claims user provisioned for this run.</param>
        /// <returns>FieldAuthorizationResult.</returns>
        private async Task <FieldAuthorizationResult> AuthorizeRequest(IGraphFieldAuthorizationRequest authRequest, ClaimsPrincipal claimsUser)
        {
            Validation.ThrowIfNull(authRequest?.Field, nameof(authRequest.Field));

            var securityGroups = authRequest.Field.SecurityGroups;

            if (securityGroups == null || !securityGroups.Any())
            {
                return(FieldAuthorizationResult.Skipped());
            }

            // each security group represents one level of security requirements (e.g. the controller group then the action)
            // the user must autenticate to each of them in turn.
            foreach (var group in securityGroups)
            {
                if (group.AllowAnonymous)
                {
                    continue;
                }

                // dont inspect these services before this
                // point in case the only security requirements set are all "allow anonymous"
                // in which case auth services don't matter
                if (claimsUser?.Identity == null)
                {
                    return(FieldAuthorizationResult.Fail("The request contains no user context to validate."));
                }

                if (!claimsUser.Identity.IsAuthenticated)
                {
                    return(FieldAuthorizationResult.Fail($"The supplied {nameof(ClaimsPrincipal)} was not successfully authenticated."));
                }

                foreach (var rule in group)
                {
                    if (rule.IsNamedPolicy)
                    {
                        if (_authService == null)
                        {
                            return(FieldAuthorizationResult.Fail(
                                       "The field defines authorization policies but " +
                                       $"no '{nameof(IAuthorizationService)}' exists to process them."));
                        }

                        // policy check via the authorization service
                        var authResult = await _authService.AuthorizeAsync(claimsUser, rule.PolicyName).ConfigureAwait(false);

                        if (!authResult.Succeeded)
                        {
                            return(FieldAuthorizationResult.Fail($"Access denied via policy '{rule.PolicyName}'."));
                        }
                    }

                    if (rule.AllowedRoles.Count > 0)
                    {
                        // check any defined roles
                        if (rule.AllowedRoles.All(x => !claimsUser.IsInRole(x)))
                        {
                            return(FieldAuthorizationResult.Fail("Access denied due to missing a required role."));
                        }
                    }

                    if (rule.AuthenticationSchemes.Count > 0)
                    {
                        // check against any limiting authentication schemes
                        if (claimsUser.Identities.All(x => !rule.AuthenticationSchemes.Contains(x.AuthenticationType)))
                        {
                            return(FieldAuthorizationResult.Fail("Access denied due to missing a required authentication scheme."));
                        }
                    }

                    // all checks passed
                }
            }

            return(FieldAuthorizationResult.Success());
        }
コード例 #6
0
 private void AssertAuthorizationSkipped(FieldAuthorizationResult result)
 {
     Assert.IsNotNull(result);
     Assert.AreEqual(FieldAuthorizationStatus.Skipped, result.Status);
     Assert.IsTrue(string.IsNullOrWhiteSpace(result.LogMessage));
 }
コード例 #7
0
 private void AssertAuthorizationFails(FieldAuthorizationResult result)
 {
     Assert.IsNotNull(result);
     Assert.AreEqual(FieldAuthorizationStatus.Unauthorized, result.Status);
     Assert.IsFalse(string.IsNullOrWhiteSpace(result.LogMessage));
 }