Skip to content

Commit

Permalink
Migrated authorization directives back into the ASP.NET Core packages. (
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib committed Sep 10, 2020
1 parent 93f21fa commit a069057
Show file tree
Hide file tree
Showing 42 changed files with 2,383 additions and 13 deletions.
30 changes: 30 additions & 0 deletions src/HotChocolate/AspNetCore/HotChocolate.AspNetCore.sln
Expand Up @@ -47,6 +47,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{604427
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StarWars", "sample\StarWars\StarWars.csproj", "{8BDA483D-AAC2-4885-8769-22BBB76BD732}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.AspNetCore.Authorization", "src\AspNetCore.Authorization\HotChocolate.AspNetCore.Authorization.csproj", "{3DA60297-97CB-4712-89C6-F71EB75F98EA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.AspNetCore.Authorization.Tests", "test\AspNetCore.Authorization.Tests\HotChocolate.AspNetCore.Authorization.Tests.csproj", "{CBADBC3F-FACC-424C-8E7D-28A029F5C238}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -237,6 +241,30 @@ Global
{8BDA483D-AAC2-4885-8769-22BBB76BD732}.Release|x64.Build.0 = Release|Any CPU
{8BDA483D-AAC2-4885-8769-22BBB76BD732}.Release|x86.ActiveCfg = Release|Any CPU
{8BDA483D-AAC2-4885-8769-22BBB76BD732}.Release|x86.Build.0 = Release|Any CPU
{3DA60297-97CB-4712-89C6-F71EB75F98EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DA60297-97CB-4712-89C6-F71EB75F98EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DA60297-97CB-4712-89C6-F71EB75F98EA}.Debug|x64.ActiveCfg = Debug|Any CPU
{3DA60297-97CB-4712-89C6-F71EB75F98EA}.Debug|x64.Build.0 = Debug|Any CPU
{3DA60297-97CB-4712-89C6-F71EB75F98EA}.Debug|x86.ActiveCfg = Debug|Any CPU
{3DA60297-97CB-4712-89C6-F71EB75F98EA}.Debug|x86.Build.0 = Debug|Any CPU
{3DA60297-97CB-4712-89C6-F71EB75F98EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DA60297-97CB-4712-89C6-F71EB75F98EA}.Release|Any CPU.Build.0 = Release|Any CPU
{3DA60297-97CB-4712-89C6-F71EB75F98EA}.Release|x64.ActiveCfg = Release|Any CPU
{3DA60297-97CB-4712-89C6-F71EB75F98EA}.Release|x64.Build.0 = Release|Any CPU
{3DA60297-97CB-4712-89C6-F71EB75F98EA}.Release|x86.ActiveCfg = Release|Any CPU
{3DA60297-97CB-4712-89C6-F71EB75F98EA}.Release|x86.Build.0 = Release|Any CPU
{CBADBC3F-FACC-424C-8E7D-28A029F5C238}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CBADBC3F-FACC-424C-8E7D-28A029F5C238}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CBADBC3F-FACC-424C-8E7D-28A029F5C238}.Debug|x64.ActiveCfg = Debug|Any CPU
{CBADBC3F-FACC-424C-8E7D-28A029F5C238}.Debug|x64.Build.0 = Debug|Any CPU
{CBADBC3F-FACC-424C-8E7D-28A029F5C238}.Debug|x86.ActiveCfg = Debug|Any CPU
{CBADBC3F-FACC-424C-8E7D-28A029F5C238}.Debug|x86.Build.0 = Debug|Any CPU
{CBADBC3F-FACC-424C-8E7D-28A029F5C238}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CBADBC3F-FACC-424C-8E7D-28A029F5C238}.Release|Any CPU.Build.0 = Release|Any CPU
{CBADBC3F-FACC-424C-8E7D-28A029F5C238}.Release|x64.ActiveCfg = Release|Any CPU
{CBADBC3F-FACC-424C-8E7D-28A029F5C238}.Release|x64.Build.0 = Release|Any CPU
{CBADBC3F-FACC-424C-8E7D-28A029F5C238}.Release|x86.ActiveCfg = Release|Any CPU
{CBADBC3F-FACC-424C-8E7D-28A029F5C238}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -257,6 +285,8 @@ Global
{86C6C9FB-3AF3-4DB6-AEC2-0F3941FEA664} = {8A75EB03-3E25-4819-AE7D-1159D5AED817}
{9957A20C-4DCC-4643-B97D-ACF00D75C702} = {8A75EB03-3E25-4819-AE7D-1159D5AED817}
{8BDA483D-AAC2-4885-8769-22BBB76BD732} = {60442713-4495-435B-BBAE-FA8BF88C886A}
{3DA60297-97CB-4712-89C6-F71EB75F98EA} = {2E2070DF-95C2-48F2-A8DF-7FE3734817ED}
{CBADBC3F-FACC-424C-8E7D-28A029F5C238} = {936FF2E5-6576-4257-A7A3-F2093D44E6CD}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EAA92712-961A-4595-82AD-C031830477CC}
Expand Down
@@ -0,0 +1,8 @@
namespace HotChocolate.AspNetCore.Authorization
{
public enum ApplyPolicy
{
BeforeResolver = 0,
AfterResolver = 1
}
}
@@ -0,0 +1,24 @@
using HotChocolate.Types;

namespace HotChocolate.AspNetCore.Authorization
{
public sealed class ApplyPolicyType
: EnumType<ApplyPolicy>
{
protected override void Configure(
IEnumTypeDescriptor<ApplyPolicy> descriptor)
{
descriptor
.Name("ApplyPolicy")
.BindValuesExplicitly();

descriptor
.Value(ApplyPolicy.BeforeResolver)
.Name("BEFORE_RESOLVER");

descriptor
.Value(ApplyPolicy.AfterResolver)
.Name("AFTER_RESOLVER");
}
}
}
@@ -0,0 +1,59 @@
using System;
using System.Reflection;
using HotChocolate.Types;
using HotChocolate.Types.Descriptors;

namespace HotChocolate.AspNetCore.Authorization
{
[AttributeUsage(
AttributeTargets.Class
| AttributeTargets.Struct
| AttributeTargets.Property
| AttributeTargets.Method,
Inherited = true,
AllowMultiple = true)]
public class AuthorizeAttribute : DescriptorAttribute
{
public string? Policy { get; set; }

public string[]? Roles { get; set; }

public ApplyPolicy Apply { get; set; } = ApplyPolicy.BeforeResolver;

protected override void TryConfigure(
IDescriptorContext context,
IDescriptor descriptor,
ICustomAttributeProvider element)
{
if (descriptor is IObjectTypeDescriptor type)
{
type.Directive(CreateDirective());
}
else if (descriptor is IObjectFieldDescriptor field)
{
field.Directive(CreateDirective());
}
}

private AuthorizeDirective CreateDirective()
{
if (Policy is { })
{
return new AuthorizeDirective(
Policy,
apply: Apply);
}
else if (Roles is { })
{
return new AuthorizeDirective(
Roles,
apply: Apply);
}
else
{
return new AuthorizeDirective(
apply: Apply);
}
}
}
}
@@ -0,0 +1,107 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using HotChocolate.Language;

namespace HotChocolate.AspNetCore.Authorization
{
public sealed class AuthorizeDirective
: ISerializable
{
public AuthorizeDirective(
IReadOnlyList<string> roles,
ApplyPolicy apply = ApplyPolicy.BeforeResolver)
: this(null, roles, apply)
{ }

public AuthorizeDirective(
string? policy = null,
IReadOnlyList<string>? roles = null,
ApplyPolicy apply = ApplyPolicy.BeforeResolver)
{
Policy = policy;
Roles = roles;
Apply = apply;
}

public AuthorizeDirective(SerializationInfo info, StreamingContext context)
{
var node = info.GetValue(
nameof(DirectiveNode),
typeof(DirectiveNode))
as DirectiveNode;

if (node == null)
{
Policy = info.GetString(nameof(Policy));
Roles = info.GetValue(nameof(Roles), typeof(List<string>)) as List<string>;
Apply = (ApplyPolicy)info.GetInt16(nameof(Apply));
}
else
{
ArgumentNode policyArgument = node.Arguments
.FirstOrDefault(t => t.Name.Value == "policy");
ArgumentNode rolesArgument = node.Arguments
.FirstOrDefault(t => t.Name.Value == "roles");
ArgumentNode resolverArgument = node.Arguments
.FirstOrDefault(t => t.Name.Value == "apply");

Policy = (policyArgument is { }
&& policyArgument.Value is StringValueNode sv)
? sv.Value
: null;

if (rolesArgument is { })
{
if (rolesArgument.Value is ListValueNode lv)
{
Roles = lv.Items.OfType<StringValueNode>()
.Select(t => t.Value?.Trim())
.Where(s => !string.IsNullOrEmpty(s))
.ToArray()!;
}
else if (rolesArgument.Value is StringValueNode svn)
{
Roles = new[] { svn.Value };
}
}

Apply = ApplyPolicy.BeforeResolver;
if (resolverArgument is { }
&& resolverArgument.Value.Value is string s
&& s == "AFTER_RESOLVER")
{
Apply = ApplyPolicy.AfterResolver;
}
}
}

/// <summary>
/// Gets the policy name that determines access to the resource.
/// </summary>
public string? Policy { get; }

/// <summary>
/// Gets of roles that are allowed to access the resource.
/// </summary>
public IReadOnlyList<string>? Roles { get; }

/// <summary>
/// Gets a value indicating if the resolver has to be executed
/// before the policy is run or after the policy is run.
///
/// The before policy option is good if the actual object is needed
/// for the policy to be evaluated.
///
/// The default is BeforeResolver.
/// </summary>
public ApplyPolicy Apply { get; }

public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue(nameof(Policy), Policy);
info.AddValue(nameof(Roles), Roles?.ToList());
info.AddValue(nameof(Apply), (int)Apply);
}
}
}
@@ -0,0 +1,42 @@
using HotChocolate.Types;

namespace HotChocolate.AspNetCore.Authorization
{
public sealed class AuthorizeDirectiveType
: DirectiveType<AuthorizeDirective>
{
protected override void Configure(
IDirectiveTypeDescriptor<AuthorizeDirective> descriptor)
{
descriptor
.Name("authorize")
.Location(DirectiveLocation.Schema)
.Location(DirectiveLocation.Object)
.Location(DirectiveLocation.FieldDefinition)
.Repeatable();

descriptor.Argument(t => t.Policy)
.Description(
"The name of the authorization policy that determines " +
"access to the annotated resource.")
.Type<StringType>();

descriptor.Argument(t => t.Roles)
.Description(
"Roles that are allowed to access the " +
"annotated resource.")
.Type<ListType<NonNullType<StringType>>>();

descriptor.Argument(t => t.Apply)
.Description(
"Defines when when the resolver shall be executed." +
"By default the resolver is executed after the policy " +
"has determined that the current user is allowed to access " +
"the field.")
.Type<NonNullType<ApplyPolicyType>>()
.DefaultValue(ApplyPolicy.BeforeResolver);

descriptor.Use<AuthorizeMiddleware>();
}
}
}

0 comments on commit a069057

Please sign in to comment.