Current section

Files

Jump to
ex_oauth2_provider lib ex_oauth2_provider plug ensure_scopes.ex
Raw

lib/ex_oauth2_provider/plug/ensure_scopes.ex

defmodule ExOauth2Provider.Plug.EnsureScopes do
@moduledoc """
Use this plug to ensure that there are the correct scopes on
the token found on the connection.
### Example
alias ExOauth2Provider.Plug.EnsureScopes
# With custom handler
plug EnsureScopes, scopes: ~w(read write), handler: SomeMod,
# item:read AND item:write scopes AND :profile scope
plug EnsureScopes, scopes: ~(item:read item:write profile)
# iteam:read AND item: write scope OR :profile for the default set
plug EnsureScopes, one_of: [~(item:read item:write),
~(profile)]
# item :read AND :write for the token located in the :secret location
plug EnsureScopes, key: :secret, scopes: ~(read :write)
If the handler option is not passed, `ExOauth2Provider.Plug.ErrorHandler`
will provide the default behavior.
"""
require Logger
import Plug.Conn
alias Plug.Conn
alias ExOauth2Provider.{Plug, Scopes}
@doc false
@spec init(Keyword.t) :: Map.t
def init(opts) do
opts = Enum.into(opts, %{})
key = Map.get(opts, :key, :default)
handler = build_handler_tuple(opts)
%{handler: handler,
key: key,
scopes_sets: scopes_sets(opts)}
end
@doc false
defp scopes_sets(%{one_of: one_of}), do: one_of
defp scopes_sets(%{scopes: single_set}), do: [single_set]
defp scopes_sets(_), do: nil
@doc false
@spec call(Conn.t, Map.t) :: Map.t
def call(conn, opts) do
conn
|> Plug.current_access_token(Map.get(opts, :key))
|> check_scopes(conn, opts)
|> handle_error()
end
defp check_scopes(nil, conn, opts), do: {:error, conn, opts}
defp check_scopes(token, conn, opts) do
scopes_set = Map.get(opts, :scopes_sets)
case matches_any_scopes_set?(token, scopes_set) do
true -> {:ok, conn, opts}
false -> {:error, conn, opts}
end
end
defp matches_any_scopes_set?(_, []), do: true
defp matches_any_scopes_set?(access_token, scopes_sets) do
Enum.any?(scopes_sets, &matches_scopes?(access_token, &1))
end
defp matches_scopes?(access_token, required_scopes) do
access_token
|> Scopes.from_access_token()
|> Scopes.all?(required_scopes)
end
defp handle_error({:ok, conn, _}), do: conn
defp handle_error({:error, %Conn{params: params} = conn, opts}) do
conn = conn |> assign(:ex_oauth2_provider_failure, :unauthorized) |> halt
params = Map.merge(params, %{reason: :unauthorized})
{mod, meth} = Map.get(opts, :handler)
apply(mod, meth, [conn, params])
end
defp build_handler_tuple(%{handler: mod}), do: {mod, :unauthorized}
defp build_handler_tuple(_), do: {Plug.ErrorHandler, :unauthorized}
end