How do you authorize nested associations?

There’s a good discussion here about where to do the authorization of nested associations. I’m curious about the details of actually doing it.

I can authorize a basic action easily:

  # any user can create a document
  def authorize(:create_document, %{}, _), do: true
  # only the owner of a document can update it
  def authorize(:update_document, %{id: user_id}, %{user_id: user_id}), do: true

  # reject anything that didn't pass other checks
  def authorize(_, _, _), do: false

But it gets messy very quickly when I allow associating a document with other items…

  def authorize(:create_document, user, %{folder_ids: folder_ids}) do
    folders = Folders.list_folders(folder_ids: folder_ids)
    Enum.all?(folders, fn folder -> authorize(:update_folder, user, folder) end)

  def authorize(:update_folder, %{id: user_id}, %{user_id: user_id}), do: true

It’s even worse for an update_document, as the user_id could be in the Document or params.

How do all of you handle this? Do you even try to authorize nested data like this, or do you just do a fetch of the association by id + user_id to filter out any they don’t own?


I ended up giving up on trying to do this check via authorization. It just gets so messy- every auth library (and pattern) I’ve seen in Elixir deals with validating a user/role against an item. Trying to validate an update, which then additionally includes changes is messy.

So I’m handling this in my change_document function. I actually was already preloading the folders there and even filtering by user, so I just needed to add some tests to ensure this behavior continues.