4

我已经看到了许多关于如何在石墨烯中实施许可系统的讨论,但除了这些讨论之外,还没有看到任何明确的实际结果。关于该主题的一些讨论示例如下:

不幸的是,这些都没有推荐在石墨烯中实现权限的首选方法。有谁知道目前这样做的最佳做法是什么?

4

1 回答 1

2

首先,只有当公共接口与私有接口显着不同时,将 API 端点拆分为公共/私有才有意义。如果不是,那么您将面临代码问题的冗余。

在我们的项目中,我们提出了简单的解决方案,这似乎被许多人认为是所需的解决方案。

我们在方法上使用以下装饰器resolve

# decorators.py

def permission_required(permission):
    """ Checking permissions on per method basis. """

    def wrapped_decorator(func):
        def inner(cls, info, *args, **kwargs):
            if check_permission(permission, info.context):
                return func(cls, info, **kwargs)
            raise Exception("Permission Denied.")

        return inner

    return wrapped_decorator


def check_permission(permission, context):
    """
    Helper function to resolve permissions.
    Permission can be a string "app_name.perm_codename"
    or callable (lambda) function with user passed as an argument:
    example: lambda(user): user.username.startswith('a')
    """

    if callable(permission):
        if not permission(context.user):
            return False
    else:
        if not context.user.has_perm(permission):
            return False
    return True

您可以按如下方式使用此装饰器:

# schema.py

from . decorators import permission_required

class UserNode(DjangoObjectType):

    class Meta:
        model = User
        interfaces = (relay.Node,)
        only_fields = (
            'id', 'first_name', 'last_name',
            'email', 'username'
        )
        filter_fields = {
            'username': ['exact'],
            'id': ['exact'],
        }

    role = graphene.String(description="User's role in the system.")

    @permission_required('our_app.some_perm')
    def resolve_role(self, info, **kwargs):
        if info.context.user.username in ['dev1', 'dev2']:
            return "developer"
        if info.context.user.is_superuser:
            return "admin"
        if info.context.user.is_staff:
            return "staff"
        return "guest"

如果您没有此特定权限our_app.some_perm,您将收到以下响应:

{
  "errors": [
    {
      "message": "Permission Denied.",
      "locations": [
        {
          "line": 7,
          "column": 9
        }
      ],
      "path": [
        "userSet",
        "edges",
        0,
        "node",
        "role"
      ]
    },
    {
      "message": "Permission Denied.",
      "locations": [
        {
          "line": 7,
          "column": 9
        }
      ],
      "path": [
        "userSet",
        "edges",
        1,
        "node",
        "role"
      ]
    }
  ],
  "data": {
    "userSet": {
      "edges": [
        {
          "node": {
            "id": "VXNlck5vZGU6MQ==",
            "username": "user1",
            "role": null
          }
        },
        {
          "node": {
            "id": "VXNlck5vZGU6Mg==",
            "username": "user2",
            "role": null
          }
        }
      ]
    }
  }
}

当您需要更富有表现力的方式来检查权限时,例如使用语句检查多个权限时,请在装饰器or中使用 lambda :@required_permission


@permission_required(lambda u: u.has_perm('app.perm1') or u.has_perm('app.perm2'))
def resolve_something1(self, info, **kwargs):
    # ... do your stuff here
    return data

@permission_required(lambda user: user.username.startswith('a'))
def resolve_something2(self, info, **kwargs):
    # ... do your stuff here
    return data

于 2019-10-25T19:47:06.173 回答