Skip to content

Commit

Permalink
Selectively allow anonymous access graphql-dotnet#44
Browse files Browse the repository at this point in the history
  • Loading branch information
Gomez Tony authored and Gomez Tony committed May 8, 2019
1 parent 1e5cb8d commit 6179a97
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,32 @@ public void passes_with_multiple_policies_on_field_and_single_on_input_type()
});
}

[Fact]
public void passes_public_authorization()
{
Settings.AddPolicy("SessionPolicy", _ => _.RequireClaim("admin"));

ShouldPassRule(_ =>
{
_.Query = @"query { login(username: ""Quinn"", password: ""password"") }";
_.Schema = ProtectedQuerySchema();
_.User = CreatePrincipal();
});
}

[Fact]
public void fails_public_authorization()
{
Settings.AddPolicy("SessionPolicy", _ => _.RequireClaim("admin"));

ShouldFailRule(_ =>
{
_.Query = @"query { author(input: { name: ""Quinn"" }) }";
_.Schema = ProtectedQuerySchema();
_.User = CreatePrincipal();
});
}

private ISchema BasicSchema()
{
var defs = @"
Expand Down Expand Up @@ -281,6 +307,29 @@ private ISchema TypedSchema()
return new Schema { Query = query };
}

private ISchema ProtectedQuerySchema()
{
var query = new ObjectGraphType();
query.AuthorizeWith("SessionPolicy");

query.Field<StringGraphType>(
"login",
arguments: new QueryArguments(
new QueryArgument<StringGraphType> {Name = "username"},
new QueryArgument<StringGraphType> { Name = "password" }
),
resolve: context => "testing"
).AuthorizePublic();

query.Field<StringGraphType>(
"author",
arguments: new QueryArguments(new QueryArgument<AuthorInputType> { Name = "input" }),
resolve: context => "testing"
);

return new Schema { Query = query };
}

public class AuthorInputType : InputObjectGraphType<Author>
{
public AuthorInputType()
Expand Down
13 changes: 12 additions & 1 deletion src/GraphQL.Authorization/AuthorizationMetadataExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
Expand All @@ -10,12 +10,18 @@ namespace GraphQL.Authorization
public static class AuthorizationMetadataExtensions
{
public static readonly string PolicyKey = "Authorization__Policies";
public static readonly string PolicyPublicKey = "Authorization__Public";

public static bool RequiresAuthorization(this IProvideMetadata type)
{
return GetPolicies(type).Any();
}

public static bool PublicAuthorization(this IProvideMetadata type)
{
return type.Metadata.ContainsKey(PolicyPublicKey);
}

public static Task<AuthorizationResult> Authorize(
this IProvideMetadata type,
ClaimsPrincipal principal,
Expand All @@ -40,6 +46,11 @@ public static void AuthorizeWith(this IProvideMetadata type, params string[] pol
type.Metadata[PolicyKey] = list;
}

public static void AuthorizePublic(this IProvideMetadata type)
{
type.Metadata[PolicyPublicKey] = null;
}

public static FieldBuilder<TSourceType, TReturnType> AuthorizeWith<TSourceType, TReturnType>(
this FieldBuilder<TSourceType, TReturnType> builder, string policy)
{
Expand Down
48 changes: 37 additions & 11 deletions src/GraphQL.Authorization/AuthorizationValidationRule.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Linq;
using GraphQL.Language.AST;
using GraphQL.Types;
using GraphQL.Validation;
using System.Collections.Generic;

namespace GraphQL.Authorization
{
Expand All @@ -27,29 +27,30 @@ public INodeVisitor Validate(ValidationContext context)
// acts as if they just don't exist vs. an auth denied error
// - filtering the Schema is not currently supported

_.Match<Operation>(astType =>
{
operationType = astType.OperationType;

var type = context.TypeInfo.GetLastType();
CheckAuth(astType, type, userContext, context, operationType);
});

_.Match<ObjectField>(objectFieldAst =>
{
var argumentType = context.TypeInfo.GetArgument().ResolvedType.GetNamedType() as IComplexGraphType;
if (argumentType == null)
{
return;
}

var fieldType = argumentType.GetField(objectFieldAst.Name);
if (fieldType.PublicAuthorization())
{
return;
}

CheckAuth(objectFieldAst, fieldType, userContext, context, operationType);
});

_.Match<Field>(fieldAst =>
{
var fieldDef = context.TypeInfo.GetFieldDef();

if (fieldDef == null) return;
if (fieldDef == null || fieldDef.PublicAuthorization())
{
return;
}

// check target field
CheckAuth(fieldAst, fieldDef, userContext, context, operationType);
Expand All @@ -66,6 +67,8 @@ private void CheckAuth(
ValidationContext context,
OperationType operationType)
{
InheritParentPolicies(type, operationType, context);

if (type == null || !type.RequiresAuthorization()) return;

var result = type
Expand All @@ -83,5 +86,28 @@ private void CheckAuth(
$"You are not authorized to run this {operationType.ToString().ToLower()}.\n{errors}",
node));
}

private void InheritParentPolicies(IProvideMetadata type, OperationType operationType, ValidationContext context)
{
List<string> parentPolicies = new List<string>();

if (operationType == OperationType.Query)
{
parentPolicies = context.Schema.Query.GetPolicies();
}
else if (operationType == OperationType.Mutation)
{
parentPolicies = context.Schema.Mutation.GetPolicies();
}
else if (operationType == OperationType.Subscription)
{
parentPolicies = context.Schema.Subscription.GetPolicies();
}

if (parentPolicies.Count > 0)
{
type.AuthorizeWith(parentPolicies.ToArray());
}
}
}
}

0 comments on commit 6179a97

Please sign in to comment.