簡體   English   中英

Graphql Absinthe基於Elixir權限的可訪問字段

[英]Graphql Absinthe Elixir permission based accessible fields

定義所有用戶可能無法訪問的字段的正確方法是什么。

例如,普通用戶可以查詢用戶並查找其他用戶句柄,但只有管理員用戶才能找到他們的電子郵件地址。 用戶類型將其定義為字段,但可能無法訪問。 是否應該有一般用戶可以看到的單獨類型? 你會如何定義它?

對不起,如果不是那么清楚,我就是不具備詞匯量。

編輯: 警告: Graphql文檔不同意此方法。 謹慎使用。 無論您需要私有字段,都必須包含適當的中間件。

使用苦艾酒中間件

這是一些代碼如何做到這一點。 在此示例中,經過身份驗證的用戶可以查看電子郵件地址。 匿名用戶不能。 您可以調整邏輯以要求您需要的任何權限。

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中間件保護。 但根據上面的鏈接

中間件/ 3的一個用途是在字段上設置默認中間件,替換default_resolver宏。

通過在字段上使用中間件/ 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
    }
  }
}

您還可以使用中間件/ 3回調,因此您的對象不會太冗長

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

通過對__using __ / 1回調的一些創造性使用,您可以從主模式文件中獲取大量此類函數。

@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定義。

我希望這有助於增加答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM