ChilliCream / graphql-platform

Welcome to the home of the Hot Chocolate GraphQL server for .NET, the Strawberry Shake GraphQL client for .NET and Banana Cake Pop the awesome Monaco based GraphQL IDE.
https://chillicream.com
MIT License
5.23k stars 744 forks source link

Support for Resource-based authorization? #5442

Closed zivfuture closed 2 years ago

zivfuture commented 2 years ago

Is your feature request related to a problem?

Resource-based authorization is more reasonable then Policy-authorization,

For example: Iike Shiro , Casbin and so on .

Resource-based authorization is more clearly and easy to maintenance

Hotchocolate is great project, can we support Resource-based authorization?

The solution you'd like

public interface IPermission
{
    string Permission { get; set; }
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class PermissionAttribute : Attribute, IPermission
{}

public class Query
{
    //query operation
    [Permission("User.View")]
    public User Search(int id){...}
}

public class Mutation
{
    //mutation operation
    [Permission("User.Update")]
    [Permission("User.Edit")]
    public User Update(User user){...}
}

//request:
{
  User(id:2){
      id
      name 
      sex
  }
}

//Authorization middleware( by custom  or by any intercepter new?):
public async Task Invoke(HttpContext context, ...)
{
   //find current visiter from accesstoken
    var currentUser=claimsPrincipal.FindFirstValue(ClaimTypes.Sub); 
   //find permissions from cache by current visiter
    var permissions=findFromRedis(currentUser);

   //important,  for the request above this will get  permissions attribute : "User.View", for now i can't get the custom permission attributes 
   var permissionRequired=find Attributes from current operations(Query/Mutation),  
   //do permissions check....
}

Product

Hot Chocolate

hsluoyz commented 2 years ago

@zivfuture you can try Casbin, it has a .NET version: https://github.com/casbin/Casbin.NET

Casbin is also widely used in GraphQL scenarios: https://casbin.io/docs/graphql-middlewares, it will be easily to adapted to our scenario.

michaelstaib commented 2 years ago
image
michaelstaib commented 2 years ago

Best Practice is: It should live in your business logic ... not on the GraphQL layer.

michaelstaib commented 2 years ago

We will rework authorization for some of the next 13 dot releases. For now I will close this issue.

glen-84 commented 1 year ago

@michaelstaib,

Could we please reopen this issue?

  1. It may be a best practice, but developers don't always follow best-practices, and HC already supports authorization in the GraphQL layer.
  2. The current authorization system only allows the use of two properties, Roles and Policy – there is no clean way to specify other data like a resource name/type, action name, permission, etc. Would it be possible for the attribute and directive to store arbitrary data in a dictionary, for example? Then derived Authorize attributes could add data to that dictionary which would then be available in the IAuthorizationHandler.
michaelstaib commented 1 year ago

We will not change it. You already can do this with policys and there are no plans to change it further. If you want to add arbitrary data you could use the ContextData on the types system members. There are enough extension points for that approach. The core team does not plan any further changes to the authorization system as these are all high impact changes.

glen-84 commented 1 year ago

You already can do this with policys

Do you mean writing policies that execute an authorization check, or forcing resource/action or permission data into the Policy property?

For the former, it's not possible to pass arguments to a policy, so you'd either need a unique policy for each resource/action pair, or a single policy that extracts the resource/action data from somewhere.

For the latter, this is what I'm trying to avoid.

If you want to add arbitrary data you could use the ContextData on the types system members.

I tried a few things, but I don't know how to do this. 😞

these are all high impact changes

I don't see how adding one additional optional property is high impact. Would it affect performance to have a "state" dictionary on the directive?


I'm trying to access my own AuthorizeAttribute directly from the middleware context:

Resolver: context.Selection.Field.ResolverMember.CustomAttributes Object type (single): context.Selection.Field.RuntimeType.CustomAttributes Object type (list): context.Selection.Field.RuntimeType.GenericTypeArguments[0].CustomAttributes Field: context.Selection.Field.Member.CustomAttributes

Maybe something like this could work, I just need a way to determine if the authorization request is for a resolver, object, or field.


Edit: This would be so much cleaner if we could pass additional state to the handler. 😞