【问题标题】:Graphql Absinthe Elixir permission based accessible fieldsGraphql Absinthe Elixir 基于权限的可访问字段
【发布时间】:2019-03-20 22:55:09
【问题描述】:

定义并非所有用户都可以访问的字段的正确方法是什么。

例如,普通用户可以查询用户并找出其他用户处理的信息,但只有管理员用户才能找到他们的电子邮件地址。用户类型将其定义为字段,但可能无法访问。一般用户可以看到的内容是否应该有单独的类型?你会如何定义它?

对不起,如果不是很清楚,我只是不掌握词汇。

【问题讨论】:

    标签: elixir phoenix-framework graphql absinthe


    【解决方案1】:

    编辑: 警告: Graphql documentation 不同意这种方法。谨慎使用。无论您在哪里需要私有字段,都必须包含适当的中间件。

    使用absinthe middleware

    这里有一些代码如何做到这一点。在此示例中,经过身份验证的用户可以看到电子邮件地址。匿名用户不能。您可以调整逻辑以要求任何您想要的权限。

    defmodule MySite.Middleware.RequireAuthenticated do
      @behaviour Absinthe.Middleware
    
      @moduledoc """
      Middleware to require authenticated user
      """
    
      def call(resolution, config) do
        case resolution.context do
          %{current_user: _} ->
            resolution
          _ ->
            Absinthe.Resolution.put_result(resolution, {:error, "unauthenticated"})
        end
      end
    end
    

    然后你定义你的对象:

      object :user do
        field :id, :id
        field :username, :string 
        field :email, :string do
          middleware MySite.Middleware.RequireAuthenticated
          middleware Absinthe.Middleware.MapGet, :email
        end
      end
    

    因此,我们的字段电子邮件受到 RequireAuthenticated 中间件的保护。但是根据上面的链接

    middleware/3 的一个用途是在字段上设置默认中间件, 替换 default_resolver 宏。

    这也可以通过在现场使用 middleware/2 宏来实现。这就是为什么我们还需要添加

      middleware Absinthe.Middleware.MapGet, :email
    

    到现场的中间件列表。

    最后当我们执行查询时

    query {
      user(id: 1){
        username
        email
        id
      }
    }
    

    我们得到的响应是填充了开放字段,而保护字段无效

    {
      "errors": [
        {
          "message": "In field \"email\": unauthenticated",
          "locations": [
            {
              "line": 4,
              "column": 0
            }
          ]
        }
      ],
      "data": {
        "user": {
          "username": "MyAwesomeUsername",
          "id": "1",
          "email": null
        }
      }
    }
    

    你也可以使用 middleware/3 回调,这样你的对象就不会太冗长

      def middleware(middleware, %{identifier: :email} = field, _object) do
        [MySite.Middleware.RequireAuthenticated] ++
          [{Absinthe.Middleware.MapGet, :email}] ++
          middleware
      end
    

    稍微创造性地使用 __using__/1 回调,您可以从主架构文件中获取大量此类函数。

    【讨论】:

      【解决方案2】:

      @voger 给出了一个很棒的答案,我只是想根据接受的问题发布一个宏示例。我目前正在使用它来验证架构中的每个字段。

      这是一个宏定义:

      defmodule MyApp.Notation do
        defmacro protected_field(field, type, viewers, opts \\ []) do
          {ast, other_opts} =
            case Keyword.split(opts, [:do]) do
              {[do: ast], other_opts} ->
                {ast, other_opts}
      
              {_, other_opts} ->
                {[], other_opts}
            end
      
          auth_middleware =
            if viewers !== :public do
              quote do
                middleware(MyApp.Middleware.ProtectedField, unquote(viewers))
              end
            end
      
          quote do
            field(unquote(field), unquote(type), unquote(other_opts)) do
              unquote(auth_middleware)
              middleware(Absinthe.Middleware.MapGet, unquote(field))
              unquote(ast)
            end
          end
        end
      end
      

      然后在你的类型定义中,你可以这样做。

      import MyApp.Notation
      
      # ...
      
      object :an_object do
        protected_field(:description, :string, [User, Admin]) do
          middleware(OtherMiddleware)
          resolve(fn _, _, _ ->
            # Custom Resolve
          end)
        end
      
        protected_field(:name, :stirng, :public, resolve: &custom_resolve/2)
      end
      

      解释:

      它添加了一个我称之为viewers 的参数,我只是将它转发到我的中间件以检查用户类型是否正确。在这种情况下,我实际上有不同的模型,我称之为 AdminUser,我可以检查当前用户。这只是一种方法的示例,因此您的解决方案可能会有所不同。对于 :public 字段,我有一个特殊情况,它只是一个传递。

      这很棒,因为我可以注入带有额外参数的中间件,并将其他所有内容转发到原始 field 定义。

      我希望这有助于增加答案。

      【讨论】:

        猜你喜欢
        • 2021-07-25
        • 2020-05-29
        • 2021-10-04
        • 2020-03-17
        • 1970-01-01
        • 2015-11-20
        • 2016-03-27
        • 2019-04-24
        • 1970-01-01
        相关资源
        最近更新 更多