【问题标题】:What is the recommended best practice to separate public and private APIs in graphene-django?在 graphene-django 中分离公共和私有 API 的推荐最佳实践是什么?
【发布时间】:2020-01-25 07:34:07
【问题描述】:

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

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

【问题讨论】:

    标签: permissions graphql graphene-django


    【解决方案1】:

    首先,只有当公共接口与私有接口有显着不同时,将 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 语句检查多个权限,在@required_permission 装饰器中使用lambda:

    
    @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
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多