Reputation: 1729
In a phoenix app, I'm trying to implement authorization like this
#= Bouncers.Bouncer.check_entry(conn, %MyApp.User{})
#=> true
defmodule Bouncers.Bouncer do
alias Bouncers.Areas
def check_entry(conn, resource) do
verify(resource, conn.path_info)
end
defp verify(resource, path) do
try do
Areas.verify(resource, path)
rescue
_ -> false
end
end
end
defmodule Bouncers.Doors do
defmacro bouncer_for(model, allowed_routes) do
for route <- allowed_routes do
quote bind_quoted: [route: route, resource: model] do
def verify(resource, route), do: true
end
end
end
end
defmodule Bouncers.Areas do
require Bouncers.Doors
import Bouncers.Doors
bouncer_for %MyApp.User{}, [
["api", "users"]
]
end
bouncer_for
creates multiple verify
method for a Struct and a path_info
array
bouncer_for %MyApp.User{}, [
["api", "users"],
["api", "posts"]
]
will create
def verify(%MyApp.User{}, ["api", "users"], do: true
def verify(%MyApp.User{}, ["api", "posts"], do: true
It works for these paths
, but I'm stuck at paths that are of the format ["api", "users", _ ]
, this throws an error about unbound variable,. I would like to something like this atleast,
bouncer_for %MyApp.User{}, [
["api", "users", "*"],
]
And replace "*"
with _
inside the parameter of the method generated by the macro,. Any pointers?
Upvotes: 3
Views: 292
Reputation: 54684
You need to define your macro like this:
defmodule Bouncers.Doors do
defmacro bouncer_for(model, allowed_routes) do
for route <- allowed_routes do
quote do
def verify(unquote(model), unquote(route)), do: true
end
end
end
end
Then it should work as expected.
The reason for this is that bind_quoted
will attempt to pass down the binding (i.e. actual values) to the macro, but ["api", "users", _]
is not a valid value in itself. It is rather a pattern, which you can embed in the dynamically generated function definition using unquote
. This will behave as if you had written the pattern directly in the definition. To illustrate this problem, here is some code that is roughly equivalent to the definitions generated by the macros:
# using bind_quoted
@route ["api", "users", _] # invalid value, raises error
def verify(%MyApp.User{}, @route), do: true
# using unquote
def verify(%MyApp.User{}, ["api", "users", _]), do: true
Upvotes: 4