Skip to content

Instantly share code, notes, and snippets.

@Wigny
Created March 14, 2023 12:41
Show Gist options
  • Save Wigny/ba94273e67cffbaecc170dd4d253f2a8 to your computer and use it in GitHub Desktop.
Save Wigny/ba94273e67cffbaecc170dd4d253f2a8 to your computer and use it in GitHub Desktop.
defmodule Example.Authorization do
defprotocol Store do
def allowed?(resource, actor, action)
end
defmacro defauthorized(fun, opts) do
quote bind_quoted: [fun: Macro.escape(fun, unquote: true), opts: opts], location: :keep do
{fun_name, fun_args} = Macro.decompose_call(fun)
resource = List.first(fun_args)
action = Keyword.get(opts, :action)
as = Keyword.get(opts, :as, :"authorized_#{fun_name}")
def unquote(as)(actor, unquote_splicing(fun_args)) do
if Store.allowed?(unquote(resource), actor, unquote(action)) do
unquote(fun_name)(unquote_splicing(fun_args))
else
{:error, :unauthorized}
end
end
end
end
end
defmodule Example.Accounts.User do
defstruct [:id, :admin?, :name]
defimpl Example.Authorization.Store do
# a user is allowed to be written or deleted by an admin user
def allowed?(_user, %{admin?: true}, action) when action in ~w[write delete]a do
true
end
# a user is allowed to be written by itself
def allowed?(user, actor, :write) do
user.id == actor.id
end
# default clause
def allowed?(_user, _actor, _action) do
false
end
end
end
defmodule Example.Accounts do
import Example.Authorization
alias Example.Accounts.User
defauthorized update_user(user, attrs), action: :write
defauthorized delete_user(user), action: :delete
def update_user(%User{} = user, attrs) do
{:ok, struct(user, attrs)}
end
def delete_user(%User{} = _user) do
{:ok, nil}
end
end
@Wigny
Copy link
Author

Wigny commented Mar 14, 2023

Usage example:

alias Example.Accounts
alias Example.Accounts.User

{:ok, _user} = Accounts.authorized_update_user(%User{id: 1, name: "Admin user", admin?: true}, %User{id: 2, name: "User name", admin?: false}, %{name: "New user name"})
{:ok, nil} = Accounts.authorized_delete_user(%User{id: 1, name: "Admin user", admin?: true}, %User{id: 2, name: "New user name", admin?: false})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment