ueberauth/guardian

Guardian.Permissions.Plug Issue when using one_of option

zacksiri opened this issue · 19 comments

** (Protocol.UndefinedError) protocol Enumerable not implemented for 0.

I'm getting this error when using the Guardian.Permissions.Plug inside my controller and using the one_of option the error is pointing to line 61 of the code which is

https://github.com/ueberauth/guardian/blob/master/lib/guardian/permissions/plug.ex#L61

I downgraded to version 1.2 and everything is working normally.

Hi @zacksiri. Did you see that we had a breaking change when upgrading to version 2.0
https://github.com/ueberauth/guardian/blob/master/CHANGELOG.md#breaking-change

@Hanspagh ah I see totally missed that thank you! I will try it out.

@Hanspagh Hey I tried implementing the changes, I'm getting the same error. Actually before I saw the breaking changes I already read through the code to figure out to come to the same conclusion the only thing I didn't do was set the encoding option which I read in the code will set it to the BitwiseEncoding module by default already.

@zacksiri would be possible to create a PR replicating the issue in the tests?

It will help us to debug based on your use case, and it will easier to do some testing around the issue as well.

Otherwise, post some code snippets that could help us to know how you are using the feature.

@yordis understood, I will get to it.

@zacksiri. Did you have some code we could look at?

@Hanspagh Hey no unfortunately I haven't had the time to create a sample project yet. I have downgraded to 1.2 for now since there are some projects I need to ship. I will upgrade my project again later to see if it still persists and will note down some code snippets.

@zacksiri Sure, I would really like to fix this thought, can you by any chance post what you have as params to one_of. Something like this asuming you use plug

plug Guardian.Permissions.BitwiseEncoding. one_of: [
    %{default: [:public_profile], user_actions: [:books]},
    %{default: [:public_profile], user_actions: [:music]},
  ]

@Hanspagh Why is there a period after BitwiseEncoding in your example? Also in the documentation: https://github.com/ueberauth/guardian/blob/master/lib/guardian/permissions.ex#L88

== Compilation error in file lib/engine_web/router.ex ==
** (SyntaxError) lib/engine_web/router.ex:17: syntax error before: ensure

Shouldn't it be a comma as it was in Guardian 1.x?

I'm having trouble upgrading from Guardian 1.2.1 to 2.0, despite reviewing the changelog (thanks for the tip)

In guardian.ex:

use Guardian.Permissions.Bitwise

became:

use Guardian.Permissions, encoding: Guardian.Permissions.BitwiseEncoding

In router.ex:

plug(Guardian.Permissions.Bitwise, ensure: %{role: [:admin]})

became

plug(Guardian.Permissions.BitwiseEncoding, ensure: %{role: [:admin]})

Compile error:

== Compilation error in file lib/engine_web/router.ex ==
** (UndefinedFunctionError) function Guardian.Permissions.BitwiseEncoding.init/1 is undefined or private
    Guardian.Permissions.BitwiseEncoding.init([ensure: %{role: [:admin]}])
    (plug) lib/plug/builder.ex:304: Plug.Builder.init_module_plug/4
    (plug) lib/plug/builder.ex:288: anonymous fn/5 in Plug.Builder.compile/3
    (elixir) lib/enum.ex:1948: Enum."-reduce/3-lists^foldl/2-0-"/3
    (plug) lib/plug/builder.ex:286: Plug.Builder.compile/3

@nathany That is a good catch. It should ofcuase be a comma. I took the example strait from the documentation, so we need to fix that.

@zacksiri here is what I have in the one_of. This is from version 1.2 that doesn't work with 2.0

plug Guardian.Permissions.Bitwise,
    one_of: [
      %{customer: [:verify_own_token]},
      %{app: [:verify_own_token]}
    ]
plug Guardian.Permissions.BitwiseEncoding,
    one_of: [
      %{customer: [:verify_own_token]},
      %{app: [:verify_own_token]}
    ]

Thanks @Hanspagh. When using a comma, any idea why I'm getting this Guardian.Permissions.BitwiseEncoding.init/1 is undefined or private error (see #607 (comment))?

Yes. It is because we need to use plug(Guardian.Permissions.Plug) and not plug(Guardian.Permissions.BitwiseEncoding). I am sorry for the inconvenience. I will update the docs right a way

Is there a resolution to this? I'm seeing the same ** (Protocol.UndefinedError) protocol Enumerable not implemented for 0. for a user that doesn't have permissions for the application. In this case, I have a single-sign on system that the user may or may not have access to an app through permissions. So they're authenticated, but not authorized. (Permission set is empty for this app).
(For a user who does have permissions, it's fine.)

In 1.2 it would handle that scenario and fall into my AuthErrorHandler, in 2.0 I get the above exception.

defmodule MyAppPerm.Guardian do
  use Guardian, otp_app: :my_app,
    permissions: %{
      league: [:aaf, :cfl, :xfl],
      feeds: [:general, :fantasy, :analytics, :stats, :premium, :quality_control, :video],
      ngs: [:all],
      feature: [:override_trn],
      admin: [:notifications],
    }
  use Guardian.Permissions, encoding: Guardian.Permissions.BitwiseEncoding

  def subject_for_token(%{"email" => _} = token, _claims) do
    {:ok, Jason.encode!(token)}
  end
  def subject_for_token(_resource, _claims) do
    {:error, "Unknown resource type"}
  end

  def resource_from_claims(claims) do
    Jason.decode(claims["sub"])
  end

  def build_claims(claims, _resource, opts) do
    # Ensure there's at least one claim
    {_, pems} =
      opts
      |> Keyword.get(:permissions)
      |> Map.get_and_update("league", fn(p) ->
        case p do
          nil -> {nil, []}
          v -> {v, v}
        end
      end)
      |> IO.inspect()

    encoded_claims =
      encode_permissions_into_claims!(claims, pems)
      |> IO.inspect()
    {:ok, encoded_claims}
  end

  def preferences(conn) do
    case MyAppPerm.Guardian.Plug.current_claims(conn) |> resource_from_claims() do
      {:ok, %{"preferences" => prefs}} -> prefs
      _ -> []
    end
  end
end



defmodule MyAppPerm.Guardian.AuthPipeline do
  @claims %{typ: "access"}
  use Guardian.Plug.Pipeline, otp_app: :my_app,
                              module: MyAppPerm.Guardian,
                              error_handler: MyAppPerm.Guardian.AuthErrorHandler

  plug Guardian.Plug.VerifySession, claims: @claims
  plug Guardian.Plug.EnsureAuthenticated
  plug Guardian.Plug.LoadResource, allow_blank: false

  # Must have access to one or more leagues
  plug Guardian.Permissions.Plug, one_of: [
    %{league: [:aaf]},
    %{league: [:cfl]},
    %{league: [:xfl]},
  ]
end

If I merge in a default set of permissions so that all of the keys exist in pems, then it works. That seems like a workaround to a code problem though.

default = %{
    league: [],
    feeds: [],
    ngs: [],
    feature: [],
    admin: [],
  }
  pems = Map.merge(default, Keyword.get(:permissions)
    encoded_claims =
      encode_permissions_into_claims!(claims, pems)

    {:ok, encoded_claims}

Thank you soo much @geofflane for the detailed bug report and the fixing PR

@Hanspagh Should Guardian.Permissions.Plug be mentioned in the CHANGELOG?