Last active
March 9, 2018 23:08
-
-
Save cjbell/acb2b4fa75d9add6f1ab04937d5cf159 to your computer and use it in GitHub Desktop.
Authorization Plugs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Usage in a controller | |
plug :load_and_authorize, [ | |
params: "account_id", | |
loader: &Accounts.get_account/1, | |
policy: AccountPolicy, | |
assign: :account | |
] when action in [:index] | |
# The plugs themselves | |
@doc """ | |
A plug to be used to load and authorize a specific resource. | |
The authorization policy will be passed the resource fetched (if it exists). | |
If the resource is not found, a 404 error will be returned. | |
Accepts a keyword list of options: | |
* `loader`: A method to be called to load the desired resource | |
* `params`: A single or list of params to pass to the loader | |
* `policy`: The policy module to invoke (will always accept `current_user` and `resource`) | |
* `assign`: An atom of a key to assign the resource onto | |
* `cmd`: (Optional) a command to pass to the policy | |
""" | |
def load_and_authorize(conn, options) do | |
conn | |
|> resolve_required_params(options) | |
|> load_resource(options) | |
|> authorize_resource(options) | |
end | |
defp load_resource(conn, options) do | |
loader = Keyword.get(options, :loader) | |
assign = Keyword.get(options, :assign) | |
func = :erlang.fun_info(loader) | |
params = conn.assigns[:authorization_params] | |
# Only apply the relevant number of params based on the arity of the | |
# specified function call | |
apply(func[:module], func[:name], Enum.take(params, func[:arity])) | |
|> case do | |
nil -> render_error(conn, :not_found) | |
item -> conn |> assign(assign, item) | |
end | |
end | |
defp authorize_resource(%{halted: true} = conn, _), do: conn | |
defp authorize_resource(conn, options) do | |
Keyword.get(options, :assign) | |
|> case do | |
nil -> conn | |
assign -> handle_authorize_assign(conn, assign, options) | |
end | |
end | |
defp handle_authorize_assign(conn, assign, options) do | |
policy = Keyword.get(options, :policy) | |
resource = conn.assigns[assign] | |
user = Guardian.Plug.current_resource(conn) | |
cmd = Keyword.get(options, :cmd, :access) | |
policy.allow?(user, resource, cmd) | |
|> case do | |
true -> conn | |
false -> render_error(conn, :forbidden) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment