vt-elixir / ja_resource

A behaviour to reduce boilerplate code in your JSON-API compliant Phoenix controllers without sacrificing flexibility.
Other
113 stars 33 forks source link

How to hook authorization into ja_resource? #32

Open begedin opened 8 years ago

begedin commented 8 years ago

In our project, we would like to use ja_resource but we're having issues with authorising users. In a lot of cases, the user being authorised to perform an action depends on the changeset they are trying to persist. (For example, an admin can demote another admin, but they should not be allowed to promote them to a higher role). That would mean, that, at some point, we would have to hook into the changeset generated by handle_create/update, call authorisation on that changeset and then either allow ja_resource to continue or halt everything due to lack of proper rights.

The way the whole create process works right now, it doesn't really allow us to put a plug between the loading of resource/creation of changeset and performing of action.

Ideally, we would have something like the option to split up the default ja_resource plug into two of them, so that

plug Authenticate when action in [:create, :update, :delete]
plug JaResource

could become

plug MyApp.Authenticate when action in [:create, :update, :delete]
plug JaResource.load
plug MyApp.Authorize
plug JaResource.perform

Looking at #30, we could split it even further, potentially having something like

plug MyApp.Authenticate when action in [:create, :update, :delete]
plug JaResource.load
plug MyApp.Authorize
plug JaResource.perform
plug MyApp.Track
plug JaResource.render

Any thoughts on that? Our team would definitely find it useful, but it might be overkill.

alanpeabody commented 8 years ago

@begedin Thank you for opening an issue, I appreciate the feedback! Currently I don't foresee using plugs as the way to add "hooks" because the use of assigns is the only way to pass data and isn't as clean as direct function calls.

For now I think you have 2 options:

Option 1

With out any changes to ja_resource can define your own handle_create, handle_update, and handle_delete functions. You can return an %Ecto.Changeset{} or a %Plug.Conn{} from any of them. You can do your own logic in shared functions as needed, eg:

def handle_update(conn, struct, params) do
  changeset = super(conn, struct, params) # or just create you changeset here like you would w/o ja_resource
  if Shared.authorized?(conn, changeset) do
    changeset
  else
    Shared.render_unauthorized(conn)
  end
end

You could get fancier with some meta programing if you choose.

Option 2

RE #30: I am open to callbacks that allow customization of default behavior. In this case callbacks that provided default but overridable "insert_struct", "update_struct", and "delete_struct" functions might be a hook you could use.

That said, currently this isn't a need for my team because we try to do as much logic outside of the controller as possible. I believe this is in embracing the philosophy of "services" that phoenix is moving towards. Personally I use an "interactor" pattern for most everything and keep my logic there.

jeroenhouben commented 7 years ago

I believe this is in embracing the philosophy of "services" that phoenix is moving towards.

@alanpeabody could you maybe point to a blog or article?

btw I really like JaResource. Thanks for open sourcing it!

alanpeabody commented 7 years ago

@jeroenhouben I am not aware of any blogs, Chris McCord's keynote from Elixir Conf is my primary source of information: https://confreaks.tv/videos/elixirconf2016-keynote

jeroenhouben commented 7 years ago

thx @alanpeabody - I'll go and watch that :)