diff --git a/GraphQL.Server.sln b/GraphQL.Server.sln
index fc5b78d8..086ee8e6 100644
--- a/GraphQL.Server.sln
+++ b/GraphQL.Server.sln
@@ -37,7 +37,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Transports.Subscriptions.Ab
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ui.Voyager", "src\Ui.Voyager\Ui.Voyager.csproj", "{B2C278E4-6A1A-4F83-AE53-C9469B4056EE}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "src\Core\Core.csproj", "{4872A0F3-FA1B-410B-834C-8A5653621E56}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "src\Core\Core.csproj", "{4872A0F3-FA1B-410B-834C-8A5653621E56}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Authorization.AspNetCore", "src\Authorization.AspNetCore\Authorization.AspNetCore.csproj", "{7A71AF0D-FE5F-4607-A6F6-960FD98CF840}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Authorization.AspNetCore.Tests", "tests\Authorization.AspNetCore.Tests\Authorization.AspNetCore.Tests.csproj", "{741DEEE6-FD0B-4F99-8A6F-43584B3E8D5F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -89,6 +93,14 @@ Global
{4872A0F3-FA1B-410B-834C-8A5653621E56}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4872A0F3-FA1B-410B-834C-8A5653621E56}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4872A0F3-FA1B-410B-834C-8A5653621E56}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7A71AF0D-FE5F-4607-A6F6-960FD98CF840}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7A71AF0D-FE5F-4607-A6F6-960FD98CF840}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7A71AF0D-FE5F-4607-A6F6-960FD98CF840}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7A71AF0D-FE5F-4607-A6F6-960FD98CF840}.Release|Any CPU.Build.0 = Release|Any CPU
+ {741DEEE6-FD0B-4F99-8A6F-43584B3E8D5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {741DEEE6-FD0B-4F99-8A6F-43584B3E8D5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {741DEEE6-FD0B-4F99-8A6F-43584B3E8D5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {741DEEE6-FD0B-4F99-8A6F-43584B3E8D5F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/Authorization.AspNetCore/Authorization.AspNetCore.csproj b/src/Authorization.AspNetCore/Authorization.AspNetCore.csproj
new file mode 100644
index 00000000..dd1421c0
--- /dev/null
+++ b/src/Authorization.AspNetCore/Authorization.AspNetCore.csproj
@@ -0,0 +1,28 @@
+
+
+
+ netstandard2.0
+ GraphQL.Server.Authorization.AspNetCore
+ GraphQL.Server.Authorization.AspNetCore
+ graphql-dotnet server
+ graphql-dotnet
+ Pekka Heikura
+ HTTP authorization middleware for graphql
+ https://github.com/graphql-dotnet/server
+ https://github.com/graphql-dotnet/server
+ Git
+ GraphQL authentication authorization middleware
+ Pekka Heikura
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Authorization.AspNetCore/AuthorizationMetadataExtensions.cs b/src/Authorization.AspNetCore/AuthorizationMetadataExtensions.cs
new file mode 100644
index 00000000..8e73a738
--- /dev/null
+++ b/src/Authorization.AspNetCore/AuthorizationMetadataExtensions.cs
@@ -0,0 +1,34 @@
+using System.Collections.Generic;
+using GraphQL.Builders;
+using GraphQL.Types;
+
+namespace GraphQL.Server.Authorization.AspNetCore
+{
+ public static class AuthorizationMetadataExtensions
+ {
+ public const string PolicyKey = "Authorization__Policies";
+
+ public static bool RequiresAuthorization(this IProvideMetadata type)
+ {
+ var policies = GetPolicies(type);
+ return policies != null && policies.Count > 0;
+ }
+
+ public static void AuthorizeWith(this IProvideMetadata type, string policy)
+ {
+ var list = GetPolicies(type) ?? new List();
+ list.Fill(policy);
+ type.Metadata[PolicyKey] = list;
+ }
+
+ public static FieldBuilder AuthorizeWith(
+ this FieldBuilder builder, string policy)
+ {
+ builder.FieldType.AuthorizeWith(policy);
+ return builder;
+ }
+
+ public static List GetPolicies(this IProvideMetadata type) =>
+ type.GetMetadata>(PolicyKey, null);
+ }
+}
\ No newline at end of file
diff --git a/src/Authorization.AspNetCore/AuthorizationValidationRule.cs b/src/Authorization.AspNetCore/AuthorizationValidationRule.cs
new file mode 100644
index 00000000..67fb78de
--- /dev/null
+++ b/src/Authorization.AspNetCore/AuthorizationValidationRule.cs
@@ -0,0 +1,173 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using GraphQL.Language.AST;
+using GraphQL.Types;
+using GraphQL.Validation;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Authorization.Infrastructure;
+using Microsoft.AspNetCore.Http;
+
+namespace GraphQL.Server.Authorization.AspNetCore
+{
+ public class AuthorizationValidationRule : IValidationRule
+ {
+ private readonly IAuthorizationService _authorizationService;
+ private readonly IHttpContextAccessor _httpContextAccessor;
+
+ public AuthorizationValidationRule(
+ IAuthorizationService authorizationService,
+ IHttpContextAccessor httpContextAccessor)
+ {
+ _authorizationService = authorizationService;
+ _httpContextAccessor = httpContextAccessor;
+ }
+
+ public INodeVisitor Validate(ValidationContext context)
+ {
+ return new EnterLeaveListener(_ =>
+ {
+ var operationType = OperationType.Query;
+
+ // this could leak info about hidden fields or types in error messages
+ // it would be better to implement a filter on the Schema so it
+ // acts as if they just don't exist vs. an auth denied error
+ // - filtering the Schema is not currently supported
+
+ _.Match(astType =>
+ {
+ operationType = astType.OperationType;
+
+ var type = context.TypeInfo.GetLastType();
+ AuthorizeAsync(astType, type, context, operationType).GetAwaiter().GetResult();
+ });
+
+ _.Match(objectFieldAst =>
+ {
+ var argumentType = context.TypeInfo.GetArgument().ResolvedType.GetNamedType() as IComplexGraphType;
+ if (argumentType == null)
+ {
+ return;
+ }
+
+ var fieldType = argumentType.GetField(objectFieldAst.Name);
+ AuthorizeAsync(objectFieldAst, fieldType, context, operationType).GetAwaiter().GetResult();
+ });
+
+ _.Match(fieldAst =>
+ {
+ var fieldDef = context.TypeInfo.GetFieldDef();
+ if (fieldDef == null)
+ {
+ return;
+ }
+
+ // check target field
+ AuthorizeAsync(fieldAst, fieldDef, context, operationType).GetAwaiter().GetResult();
+ // check returned graph type
+ AuthorizeAsync(fieldAst, fieldDef.ResolvedType, context, operationType).GetAwaiter().GetResult();
+ });
+ });
+ }
+
+ private async Task AuthorizeAsync(
+ INode node,
+ IProvideMetadata type,
+ ValidationContext context,
+ OperationType operationType)
+ {
+ if (type == null || !type.RequiresAuthorization())
+ {
+ return;
+ }
+
+ var policyNames = type.GetPolicies();
+ if (policyNames.Count == 0)
+ {
+ return;
+ }
+
+ var tasks = new List>(policyNames.Count);
+ foreach (var policyName in policyNames)
+ {
+ var task = _authorizationService.AuthorizeAsync(this._httpContextAccessor.HttpContext.User, policyName);
+ tasks.Add(task);
+ }
+ await Task.WhenAll(tasks);
+
+ foreach (var task in tasks)
+ {
+ var result = task.Result;
+ if (!result.Succeeded)
+ {
+ var stringBuilder = new StringBuilder("You are not authorized to run this ");
+ stringBuilder.Append(operationType.ToString().ToLower());
+ stringBuilder.AppendLine(".");
+
+ foreach (var failure in result.Failure.FailedRequirements)
+ {
+ AppendFailureLine(stringBuilder, failure);
+ }
+
+ context.ReportError(
+ new ValidationError(context.OriginalQuery, "authorization", stringBuilder.ToString(), node));
+ }
+ }
+ }
+
+ private static void AppendFailureLine(
+ StringBuilder stringBuilder,
+ IAuthorizationRequirement authorizationRequirement)
+ {
+ switch (authorizationRequirement)
+ {
+ case ClaimsAuthorizationRequirement claimsAuthorizationRequirement:
+ stringBuilder.Append("Required claim '");
+ stringBuilder.Append(claimsAuthorizationRequirement.ClaimType);
+ if (claimsAuthorizationRequirement.AllowedValues == null || !claimsAuthorizationRequirement.AllowedValues.Any())
+ {
+ stringBuilder.AppendLine("' is not present.");
+ }
+ else
+ {
+ stringBuilder.Append("' with any value of '");
+ stringBuilder.Append(string.Join(", ", claimsAuthorizationRequirement.AllowedValues));
+ stringBuilder.AppendLine("' is not present.");
+ }
+ break;
+ case DenyAnonymousAuthorizationRequirement denyAnonymousAuthorizationRequirement:
+ stringBuilder.AppendLine("The current user must be authenticated.");
+ break;
+ case NameAuthorizationRequirement nameAuthorizationRequirement:
+ stringBuilder.Append("The current user name must match the name '");
+ stringBuilder.Append(nameAuthorizationRequirement.RequiredName);
+ stringBuilder.AppendLine("'.");
+ break;
+ case OperationAuthorizationRequirement operationAuthorizationRequirement:
+ stringBuilder.Append("Required operation '");
+ stringBuilder.Append(operationAuthorizationRequirement.Name);
+ stringBuilder.AppendLine("' was not present.");
+ break;
+ case RolesAuthorizationRequirement rolesAuthorizationRequirement:
+ if (rolesAuthorizationRequirement.AllowedRoles == null || !rolesAuthorizationRequirement.AllowedRoles.Any())
+ {
+ // This should never happen.
+ stringBuilder.AppendLine("Required roles are not present.");
+ }
+ else
+ {
+ stringBuilder.Append("Required roles '");
+ stringBuilder.Append(string.Join(", ", rolesAuthorizationRequirement.AllowedRoles));
+ stringBuilder.AppendLine("' are not present.");
+ }
+ break;
+ default:
+ stringBuilder.Append("Requirement '");
+ stringBuilder.Append(authorizationRequirement.GetType().Name);
+ stringBuilder.AppendLine("' was not satisfied.");
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Authorization.AspNetCore/GraphQLAuthorizeAttribute.cs b/src/Authorization.AspNetCore/GraphQLAuthorizeAttribute.cs
new file mode 100644
index 00000000..b28d6510
--- /dev/null
+++ b/src/Authorization.AspNetCore/GraphQLAuthorizeAttribute.cs
@@ -0,0 +1,19 @@
+using GraphQL.Utilities;
+
+namespace GraphQL.Server.Authorization.AspNetCore
+{
+ public class GraphQLAuthorizeAttribute : GraphQLAttribute
+ {
+ public string Policy { get; set; }
+
+ public override void Modify(TypeConfig type)
+ {
+ type.AuthorizeWith(Policy);
+ }
+
+ public override void Modify(FieldConfig field)
+ {
+ field.AuthorizeWith(Policy);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Authorization.AspNetCore/GraphQlBuilderExtensions.cs b/src/Authorization.AspNetCore/GraphQlBuilderExtensions.cs
new file mode 100644
index 00000000..2123ace4
--- /dev/null
+++ b/src/Authorization.AspNetCore/GraphQlBuilderExtensions.cs
@@ -0,0 +1,44 @@
+using System;
+using GraphQL.Server.Authorization.AspNetCore;
+using GraphQL.Validation;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+
+namespace GraphQL.Server
+{
+ public static class GraphQLBuilderExtensions
+ {
+ ///
+ /// Adds the GraphQL authorization.
+ ///
+ /// The GraphQL builder.
+ ///
+ public static IGraphQLBuilder AddGraphQLAuthorization(this IGraphQLBuilder builder)
+ {
+ builder.Services.TryAddSingleton();
+ builder
+ .Services
+ .AddTransient()
+ .AddAuthorization();
+ return builder;
+ }
+
+ ///
+ /// Adds the GraphQL authorization.
+ ///
+ /// The GraphQL builder.
+ /// An action delegate to configure the provided .
+ /// The GraphQL builder.
+ public static IGraphQLBuilder AddGraphQLAuthorization(this IGraphQLBuilder builder, Action options)
+ {
+ builder.Services.TryAddSingleton();
+ builder
+ .Services
+ .AddTransient()
+ .AddAuthorization(options);
+ return builder;
+ }
+ }
+}
diff --git a/tests/Authorization.AspNetCore.Tests/Authorization.AspNetCore.Tests.csproj b/tests/Authorization.AspNetCore.Tests/Authorization.AspNetCore.Tests.csproj
new file mode 100644
index 00000000..b8382bba
--- /dev/null
+++ b/tests/Authorization.AspNetCore.Tests/Authorization.AspNetCore.Tests.csproj
@@ -0,0 +1,31 @@
+
+
+
+ netcoreapp2.0
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GraphQL.Server.Authorization.AspNetCore.Tests
+
+
+
+
+
+
+
diff --git a/tests/Authorization.AspNetCore.Tests/AuthorizationValidationRuleTests.cs b/tests/Authorization.AspNetCore.Tests/AuthorizationValidationRuleTests.cs
new file mode 100644
index 00000000..8847a1ab
--- /dev/null
+++ b/tests/Authorization.AspNetCore.Tests/AuthorizationValidationRuleTests.cs
@@ -0,0 +1,245 @@
+using System.Collections.Generic;
+using GraphQL.Types;
+using Xunit;
+
+namespace GraphQL.Server.Authorization.AspNetCore.Tests
+{
+ public class AuthorizationValidationRuleTests : ValidationTestBase
+ {
+ [Fact]
+ public void class_policy_success()
+ {
+ ConfigureAuthorizationOptions(
+ options =>
+ {
+ options.AddPolicy("ClassPolicy", x => x.RequireClaim("admin"));
+ });
+
+ ShouldPassRule(_ =>
+ {
+ _.Query = @"query { post }";
+ _.Schema = BasicSchema();
+ _.User = CreatePrincipal(claims: new Dictionary
+ {
+ {"Admin", "true"}
+ });
+ });
+ }
+
+ [Fact]
+ public void class_policy_fail()
+ {
+ ConfigureAuthorizationOptions(
+ options =>
+ {
+ options.AddPolicy("ClassPolicy", x => x.RequireClaim("admin"));
+ });
+
+ ShouldFailRule(_ =>
+ {
+ _.Query = @"query { post }";
+ _.Schema = BasicSchema();
+ });
+ }
+
+ [Fact]
+ public void field_policy_success()
+ {
+ ConfigureAuthorizationOptions(
+ options =>
+ {
+ options.AddPolicy("FieldPolicy", x => x.RequireClaim("admin"));
+ });
+
+ ShouldPassRule(_ =>
+ {
+ _.Query = @"query { post }";
+ _.Schema = BasicSchema();
+ _.User = CreatePrincipal(claims: new Dictionary
+ {
+ {"Admin", "true"}
+ });
+ });
+ }
+
+ [Fact]
+ public void field_policy_fail()
+ {
+ ConfigureAuthorizationOptions(
+ options =>
+ {
+ options.AddPolicy("FieldPolicy", x => x.RequireClaim("admin"));
+ });
+
+ ShouldFailRule(_ =>
+ {
+ _.Query = @"query { post }";
+ _.Schema = BasicSchema();
+ });
+ }
+
+ [Fact]
+ public void nested_type_policy_success()
+ {
+ ConfigureAuthorizationOptions(
+ options =>
+ {
+ options.AddPolicy("PostPolicy", x => x.RequireClaim("admin"));
+ });
+
+ ShouldPassRule(_ =>
+ {
+ _.Query = @"query { post }";
+ _.Schema = NestedSchema();
+ _.User = CreatePrincipal(claims: new Dictionary
+ {
+ {"Admin", "true"}
+ });
+ });
+ }
+
+ [Fact]
+ public void nested_type_policy_fail()
+ {
+ ConfigureAuthorizationOptions(
+ options =>
+ {
+ options.AddPolicy("PostPolicy", x => x.RequireClaim("admin"));
+ });
+
+ ShouldFailRule(_ =>
+ {
+ _.Query = @"query { post }";
+ _.Schema = NestedSchema();
+ });
+ }
+
+ [Fact]
+ public void passes_with_claim_on_input_type()
+ {
+ ConfigureAuthorizationOptions(
+ options =>
+ {
+ options.AddPolicy("FieldPolicy", x => x.RequireClaim("admin"));
+ });
+
+ ShouldPassRule(_ =>
+ {
+ _.Query = @"query { author(input: { name: ""Quinn"" }) }";
+ _.Schema = TypedSchema();
+ _.User = CreatePrincipal(claims: new Dictionary
+ {
+ {"Admin", "true"}
+ });
+ });
+ }
+
+ [Fact]
+ public void fails_on_missing_claim_on_input_type()
+ {
+ ConfigureAuthorizationOptions(
+ options =>
+ {
+ options.AddPolicy("FieldPolicy", x => x.RequireClaim("admin"));
+ });
+
+ ShouldFailRule(_ =>
+ {
+ _.Query = @"query { author(input: { name: ""Quinn"" }) }";
+ _.Schema = TypedSchema();
+ });
+ }
+
+ private ISchema BasicSchema()
+ {
+ string defs = @"
+ type Query {
+ post(id: ID!): String
+ }
+ ";
+
+ return Schema.For(defs, _ =>
+ {
+ _.Types.Include();
+ });
+ }
+
+ [GraphQLMetadata("Query")]
+ [GraphQLAuthorize(Policy = "ClassPolicy")]
+ public class BasicQueryWithAttributesAndClassPolicy
+ {
+ public string Post(string id)
+ {
+ return "";
+ }
+ }
+
+ [GraphQLMetadata("Query")]
+ public class BasicQueryWithAttributesAndFieldPolicy
+ {
+ [GraphQLAuthorize(Policy = "FieldPolicy")]
+ public string Post(string id)
+ {
+ return "";
+ }
+ }
+
+ private ISchema NestedSchema()
+ {
+ string defs = @"
+ type Query {
+ post(id: ID!): Post
+ }
+
+ type Post {
+ id: ID!
+ }
+ ";
+
+ return Schema.For(defs, _ =>
+ {
+ _.Types.Include();
+ _.Types.Include();
+ });
+ }
+
+ [GraphQLMetadata("Query")]
+ public class NestedQueryWithAttributes
+ {
+ public Post Post(string id)
+ {
+ return null;
+ }
+ }
+
+ [GraphQLAuthorize(Policy = "PostPolicy")]
+ public class Post
+ {
+ public string Id { get; set; }
+ }
+
+ public class Author
+ {
+ public string Name { get; set; }
+ }
+
+ private ISchema TypedSchema()
+ {
+ var query = new ObjectGraphType();
+ query.Field(
+ "author",
+ arguments: new QueryArguments(new QueryArgument { Name = "input" }),
+ resolve: context => "testing"
+ );
+ return new Schema { Query = query };
+ }
+
+ public class AuthorInputType : InputObjectGraphType
+ {
+ public AuthorInputType()
+ {
+ Field(x => x.Name).AuthorizeWith("FieldPolicy");
+ }
+ }
+ }
+}
diff --git a/tests/Authorization.AspNetCore.Tests/ValidationTestBase.cs b/tests/Authorization.AspNetCore.Tests/ValidationTestBase.cs
new file mode 100644
index 00000000..ad3c03d6
--- /dev/null
+++ b/tests/Authorization.AspNetCore.Tests/ValidationTestBase.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Claims;
+using GraphQL.Execution;
+using GraphQL.Http;
+using GraphQL.Types;
+using GraphQL.Validation;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Shouldly;
+
+namespace GraphQL.Server.Authorization.AspNetCore.Tests
+{
+ public class ValidationTestConfig
+ {
+ private readonly List _rules = new List();
+
+ public string Query { get; set; }
+ public ISchema Schema { get; set; }
+ public IEnumerable Rules => _rules;
+ public ClaimsPrincipal User { get; set; }
+ public Inputs Inputs { get; set; }
+
+ public void Rule(params IValidationRule[] rules)
+ {
+ _rules.AddRange(rules);
+ }
+ }
+ public class GraphQLUserContext
+ {
+ public ClaimsPrincipal User { get; set; }
+ }
+ public class ValidationTestBase
+ {
+ private IDocumentExecuter _executor = new DocumentExecuter();
+ private IDocumentWriter _writer = new DocumentWriter(indent: true);
+
+ protected HttpContext HttpContext { get; private set; }
+
+ protected AuthorizationValidationRule Rule { get; private set; }
+
+ protected void ConfigureAuthorizationOptions(Action setupOptions)
+ {
+ var (authorizationService, httpContextAccessor) = BuildServices(setupOptions);
+ HttpContext = httpContextAccessor.HttpContext;
+ Rule = new AuthorizationValidationRule(authorizationService, httpContextAccessor);
+ }
+
+ protected void ShouldPassRule(Action configure)
+ {
+ var config = new ValidationTestConfig();
+ config.Rule(Rule);
+ configure(config);
+
+ config.Rules.Any().ShouldBeTrue("Must provide at least one rule to validate against.");
+
+ config.Schema.Initialize();
+
+ var result = Validate(config);
+
+ var message = "";
+ if (result.Errors?.Any() == true)
+ {
+ message = string.Join(", ", result.Errors.Select(x => x.Message));
+ }
+ result.IsValid.ShouldBeTrue(message);
+ }
+
+ protected void ShouldFailRule(Action configure)
+ {
+ var config = new ValidationTestConfig();
+ config.Rule(Rule);
+ configure(config);
+
+ config.Rules.Any().ShouldBeTrue("Must provide at least one rule to validate against.");
+
+ config.Schema.Initialize();
+
+ var result = Validate(config);
+
+ result.IsValid.ShouldBeFalse("Expected validation errors though there were none.");
+ }
+
+ private (IAuthorizationService, IHttpContextAccessor) BuildServices(Action setupOptions)
+ {
+ var services = new ServiceCollection();
+ services.AddAuthorization(setupOptions);
+ services.AddLogging();
+ services.AddOptions();
+ services.AddSingleton();
+ var serviceProvider = services.BuildServiceProvider();
+ var authorizationService = serviceProvider.GetRequiredService();
+ var httpContextAccessor = serviceProvider.GetRequiredService();
+ httpContextAccessor.HttpContext = new DefaultHttpContext();
+ return (authorizationService, httpContextAccessor);
+ }
+
+ private IValidationResult Validate(ValidationTestConfig config)
+ {
+ this.HttpContext.User = config.User;
+ var userContext = new GraphQLUserContext { User = config.User };
+ var documentBuilder = new GraphQLDocumentBuilder();
+ var document = documentBuilder.Build(config.Query);
+ var validator = new DocumentValidator();
+ return validator.Validate(config.Query, config.Schema, document, config.Rules, userContext, config.Inputs);
+ }
+
+ protected ClaimsPrincipal CreatePrincipal(string authenticationType = null, IDictionary claims = null)
+ {
+ var claimsList = new List();
+
+ claims?.Apply(c =>
+ {
+ claimsList.Add(new Claim(c.Key, c.Value));
+ });
+
+ return new ClaimsPrincipal(new ClaimsIdentity(claimsList, authenticationType));
+ }
+ }
+}