BeaconCMS / beacon

Open-source content management system (CMS) built with Phoenix LiveView. Faster render times to boost SEO performance, even for the most content-heavy pages.
https://beaconcms.org
MIT License
1.04k stars 101 forks source link

Authz #564

Open leandrocp opened 4 months ago

leandrocp commented 4 months ago

Authorize on both admin and core

Visiting an admin page or submitting data on beacon_live_admin should be authorized along with the context call on beacon, to avoid bypassing authorization rules. Currently only Admin performs authorizations.

Add authz into core

APIs like Content should have an authz layer. Since it's optional and would be added after the v0.1 release, it's better to add as an optional keyword argument:

def publish_page(attrs, opts \\ [])
  # returns the actor and context?
  authz = Keyword.get(opts, :authz)
  # or some other name
end

This API has some similarities with https://ash-hq.org/docs/guides/ash/latest/actors-and-authorization

Rename agent

Rename to actor. More common and familiar than agent.

Resolve actor

On Admin the actor is resolved and assigned into the socket based on the data in the session, see https://github.com/BeaconCMS/beacon_live_admin/blob/66d5bc1e1249bc58c8c04bf4840e55812a43f082/lib/beacon/live_admin/hooks/assign_agent.ex so all subsequent requests will have it available. That's the reason there's a callback get_agent in the policy module. But that doesn't work well in Beacon since there's no persisted state like the socket. Need to review this concept, but most likely will keep the actor in the admin socket and pass it as argument to beacon (core) calls.

Rename authorized?

Rename to authorize. Shorter and makes more sense because the function is indeed a command.

Remove DefaultPolicy

Just leave the config option empty and returns true to all authorize calls immediately. Less code, less cycles.

Augment action details

Pass to the policy module not only the actor and the origin module but also the resource data. So if one is publishing a page, also pass the page data to let the policy module authorize based on its content, for example only a blog_editor is authorized to publish pages of type "blog_post"

Authz on custom admin pages

Custom admin pages may also add authz using the same system so those pages need to call authorize to perform actions. Exposing that API means we need to make sure it cover the needs of custom pages and documentation for thojse custom actions are available as well.

Alerts and feedback

Currently failing to authorize an action will just redirect to admin home or return {:noreply, socket} which is not good UX. We need to provide feedback to users and https://github.com/BeaconCMS/beacon_live_admin/issues/175 might resolve it.

Docs

Concentrate all authorization calls into a single place so it becomes trivial to find all existing actions for documentation purposes. For example we should be able to tell that publishing a page will authorize the :publish_page action passing the actor and the %Page of such action as additional context. Something like:

defmodule Beacon.Authz.Authorizations do
  def authorize(:publish_page, actor, %{page: %Page{}})
  def authorize(...)
end

Admin probably should make use of this strategy too.

Extra

See https://github.com/BeaconCMS/beacon/pull/563 and https://github.com/BeaconCMS/beacon_live_admin/pull/189