Plans for V3 #82
Replies: 5 comments 7 replies
-
This is a great setup for a discussion! ❤️ |
Beta Was this translation helpful? Give feedback.
-
I don't know if it's a rough edge per se, but here's a specific requirement that we had to do a little work to solve. (Unless we missed something obvious!) We wanted to make sure that Here's the way it came together for us... # my_app/policy.ex
defmodule MyApp.Policy do
# This is a base policy of sorts
@behaviour Bodyguard.Policy
# Set behaviour and generate "catch-all" authorize
defmacro __using__(_opts) do
quote do
@behaviour Bodyguard.Policy
@before_compile MyApp.Policy
end
end
# Add catch-all
defmacro __before_compile__(_env) do
quote do
# Define a catch-all authorize *at the end* of the module. This will deny and
# handle the missed pattern match according to application config.
def authorize(action, user, params) do
MyApp.Policy.authorize(action, user, params, __MODULE__)
end
end
end
# Handle per config and deny
def authorize(action, user, params, module \\ __MODULE__) do
config = Application.get_env(:my_app, __MODULE__, [])
message = "Unmatched authorize call for #{inspect([module, action, user, params])}"
if Keyword.get(config, :unmatched_authorize) == :raise,
do: raise(UnmatchedAuthorizeError, message: message),
else: Logger.warn(message)
:error
end
end # my_app/foos.ex
defmodule MyApp.Foos do
defdelegate authorize(action, user, params), to: MyApp.Foos.Policy
# regular context stuff ...
end # my_app/foos/policy.ex
defmodule MyApp.Foos.Policy do
use MyApp.Policy
# regular policy stuff ...
# => catch-all generated here with before_compile
end |
Beta Was this translation helpful? Give feedback.
-
😄
👍
👍
👍 We do use them, but I agree.
👍
I'm interested in what you have in mind for this. Hooks to move authorization out of the main Options A and B seem best to me. I think a big part of the value of the library is "this is the way". |
Beta Was this translation helpful? Give feedback.
-
I've been doing some soul-searching on the type spec for new callback as well. Here's where I've landed so far defmodule Bodyguard.Policy do
@callback permit?(user :: term, action :: atom | String.t(), context :: map) :: boolean
end Argument orderingWith the But having current_user
|> MyPolicy.permit?(:edit, %{post: post})
|> case do
# ...
end and it sort of reads nicer, like "permit user X to perform action Y on thing Z". Rethinking
|
Beta Was this translation helpful? Give feedback.
-
Please go with C (or B)! For context, I'm new to Elixir/Phoenix coming from a JavaScript and Rails world, and I really dislike some of the magic that the libraries have. The main problem I have with the ecosystem in Elixir is that there isn't enough documentation for many libraries. However, if it's all generated then I can just play around with it, debug, and learn it more quickly. I would like to share my experience with |
Beta Was this translation helpful? Give feedback.
-
I'd like to explore some options for how Bodyguard can evolve in the next major revision. I think there is still a place in the ecosystem for a standardized way to perform authorization, but surely it doesn't need to be as complicated and opinionated as how Bodyguard currently works.
When I peruse the codebase and the docs, I'm struck by a few negative aspects:
Authorize
plugpermit
functions and theauthorize
callbacks seems like an unnecessary impedance mismatchAre there any other problems or rough edges that people see?
Here are a few options for how V3 (or a new library??) could function.
Option A - Very simple core, opt-in extensions
In this model, there would be a core
bodyguard
library with no dependencies, which basically defines a behaviour and maybe has some very simple wrapper or handler functions.Additional functionality could be opted in using other libraries like
bodyguard_plug
,bodyguard_liveview
,bodyguard_phoenix
,bodyguard_absinthe
, etc etcOption B - Very simple core with generators
In this version, there would still be a basic
Bodyguard.Policy
behaviour to hand your authorization hat on, but all integrations INTO the policies would be created by mix generators.This solves a lot of my complexity gripes, while still pushing developers down a common road.
Option C - All-in on generators
Why do we need a behaviour at all? It's all just functions, man!
This has the benefit of being the absolute most flexible and future-proof implementation for nearly all use cases, but at the risk of losing sight of what value this library even provides at all.
Thoughts? Concerns? Ideas? Let's chat.
Beta Was this translation helpful? Give feedback.
All reactions