关于GraphQL Ruby的visible?和authorized?函数

GraphQL使用Mutation和Query的对象和字段等来进行授权。
这样,无论发出什么查询,我们都可以通过检查每个字段、对象等的权限来确定用户是否具有查看权限,并返回结果。

在上下文中,我会获取用户的信息并进行认证。

在业务逻辑层进行许可设置

在这篇文章中,我们将讨论GraphQL Ruby的Visibility和Authorization。然而,在考虑使用这种GraphQL层面的授权之前,我们应该先考虑在业务逻辑层面进行授权。

 

通过在模型中编写逻辑,无需为每个字段编写相同的处理程序。

 

class Post < ActiveRecord::Base
  def self.posts_for(user)
    if user.admin?
      self.all
    else
      self.published
    end
  end
end
field :posts, [Types::Post], null: false

def posts
  Post.posts_for(context[:current_user])
end

GraphQL 在权限层面实施认证也有其优势之处。它可以进一步确保 API 层面的安全性,并且可以在执行之前进行授权。

在GraphQL Ruby中,有Visibility和Authorization功能,我将对这两个功能进行说明。

可见性 和 授权

 

每一种都可以根据用户权限设置对某个领域或对象(类型类)的可访问信息的限制。每种都具有以下特点:

    • Visibility: visible?

スキーマの一部を隠すことができる
権限のないユーザーが参照しようとするとバリデーションエラーを返す

Authorization: authorized?

権限のないユーザーも、fieldやobjectをIntrospectionで見ることができる
権限のないユーザーが参照しようとするとnilを返す

“visible?”是GraphQL Ruby的功能,而不是GraphQL规范的一部分。
它被设计用于在开发中隐藏不希望一般用户知道的功能,并仅对特定成员进行公开。

我们可以分别为object、field、引数等定义和设置visible?和authorized?方法。

领域

我们将对字段进行单元测试,以验证隐藏和授权设置。
我们在BaseField中定义visible?和authorized?方法。
当每个方法返回false时,用户将无法查看该字段。

module Types
  class BaseField < GraphQL::Schema::Field
    argument_class Types::BaseArgument

    def initialize(*args, admin_only_visible: false, admin_only_authorized: false, **kwargs, &block)
      @admin_only_visible = admin_only_visible
      @admin_only_authorized = admin_only_authorized
      super(*args, **kwargs, &block)
    end

    def visible?(ctx)
      super && (@admin_only_visible ? ctx[:current_user]&.admin? : true)
    end

    def authorized?(obj, args, ctx)
      super && (@admin_only_authorized ? ctx[:current_user]&.admin? : true)
    end
  end
end

当在需要限制的字段上分别设置admin_only_visible和admin_only_authorized时,仅管理员用户可以查看该字段。

module Types
  class UserType < Types::BaseObject
    field :id, ID, null: false
-    field :name, String, null: false
-    field :email, String, null: true
+    field :name, String, null: false, admin_only_visible: true
+    field :email, String, null: true, admin_only_authorized: true
  end
end

通过这个,可以对管理员用户进行以下参考。

image.png

如果不是管理员的话,根据name的visible?设置,字段将无法被引用并返回错误。

image.png

看到这个字段,我发现名称被隐藏了。

image.png

设置了”authorized?”的电子邮件字段不会报错,但会返回null。

image.png

物体

我們接下來試著設定Object。

module Types
  class UserType < Types::BaseObject
    field :id, ID, null: false
    field :name, String, null: false
    field :email, String, null: true
+    field :articles, ArticleType.connection_type, null: false
  end
end

module Types
  class ArticleType < Types::BaseObject
    field :id, ID, null: false
    field :title, String, null: false
    field :body, String, null: false
    field :author, Types::UserType, null: false

+    def self.visible?(context)
+      super && context[:current_user]&.admin?
+    end
+
+    # def self.authorized?(object, context)
+    #   super && context[:current_user]&.admin?
+    # end
  end
end

能看得見嗎?若是可見的話,就似乎不存在一個User的類型中的articles。
此外,不僅物件本身被隱藏,還可看出返回其類型的field也被隱藏了。

image.png

如果是经过授权的话,则不会报错,而是返回null。

image.png

授权?自定义错误。

当在设置了“authorized”属性的对象中引用字段时,通常情况下不会发生错误,而是返回null,但也可以自定义此行为。

 

通过在模式中进行设置,可以实现以下效果:

class MySchema < GraphQL::Schema
  lazy_resolve(Promise, :sync)
  mutation(Types::MutationType)
  query(Types::QueryType)
  default_max_page_size 20
  use GraphQL::Dataloader

  def self.unauthorized_object(error)
    raise GraphQL::ExecutionError, "An object of type #{error.type.graphql_name} was hidden due to permissions"
  end

  def self.unauthorized_field(error)
    raise GraphQL::ExecutionError, "The field #{error.field.graphql_name} on an object of type #{error.type.graphql_name} was hidden due to permissions"
  end
end

看起来还可以添加错误信息。

image.png

请参考

    • https://graphql.org/learn/thinking-in-graphs/#business-logic-layer

 

    https://graphql-ruby.org/authorization/overview.html
看起来GraphQL::Pro集成了CanCan和Pundit。
广告
将在 10 秒后关闭
bannerAds