Refactor Mobilizon.Federation.ActivityPub.Permission to handle

permissions

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2021-07-26 17:18:28 +02:00
parent 1ceb976f4e
commit 20ff0a7f6c
No known key found for this signature in database
GPG key ID: A061B9DDE0CA0773
11 changed files with 110 additions and 81 deletions

View file

@ -8,12 +8,42 @@ defmodule Mobilizon.Federation.ActivityPub.Permission do
alias Mobilizon.Federation.ActivityPub.Types.{Entity, Ownable} alias Mobilizon.Federation.ActivityPub.Types.{Entity, Ownable}
require Logger require Logger
use StructAccess
defstruct [:access, :create, :update, :delete]
@member_roles [:member, :moderator, :administrator]
@doc """ @doc """
Check that actor can access the object Check that actor can access the object
""" """
@spec can_access_group_object?(Actor.t(), Entity.t()) :: boolean() @spec can_access_group_object?(Actor.t(), Entity.t()) :: boolean()
def can_access_group_object?(%Actor{} = actor, object) do def can_access_group_object?(%Actor{} = actor, object) do
can_manage_group_object?(:role_needed_to_access, actor, object) can_manage_group_object?(:access, actor, object)
end
@doc """
Check that actor can create such an object
"""
@spec can_create_group_object?(String.t() | integer(), String.t() | integer(), Entity.t()) ::
boolean()
def can_create_group_object?(
actor_id,
group_id,
object
) do
case object |> Ownable.permissions() |> get_in([:create]) do
:member ->
Actors.is_member?(actor_id, group_id)
:moderator ->
Actors.is_moderator?(actor_id, group_id)
:administrator ->
Actors.is_administrator?(actor_id, group_id)
_ ->
false
end
end end
@doc """ @doc """
@ -21,7 +51,7 @@ defmodule Mobilizon.Federation.ActivityPub.Permission do
""" """
@spec can_update_group_object?(Actor.t(), Entity.t()) :: boolean() @spec can_update_group_object?(Actor.t(), Entity.t()) :: boolean()
def can_update_group_object?(%Actor{} = actor, object) do def can_update_group_object?(%Actor{} = actor, object) do
can_manage_group_object?(:role_needed_to_update, actor, object) can_manage_group_object?(:update, actor, object)
end end
@doc """ @doc """
@ -29,29 +59,31 @@ defmodule Mobilizon.Federation.ActivityPub.Permission do
""" """
@spec can_delete_group_object?(Actor.t(), Entity.t()) :: boolean() @spec can_delete_group_object?(Actor.t(), Entity.t()) :: boolean()
def can_delete_group_object?(%Actor{} = actor, object) do def can_delete_group_object?(%Actor{} = actor, object) do
can_manage_group_object?(:role_needed_to_delete, actor, object) can_manage_group_object?(:delete, actor, object)
end end
@type existing_object_permissions :: :access | :update | :delete
@spec can_manage_group_object?( @spec can_manage_group_object?(
:role_needed_to_access | :role_needed_to_update | :role_needed_to_delete, existing_object_permissions(),
Actor.t(), Actor.t(),
any() any()
) :: boolean() ) :: boolean()
defp can_manage_group_object?(action_function, %Actor{url: actor_url} = actor, object) do defp can_manage_group_object?(permission, %Actor{url: actor_url} = actor, object) do
if Ownable.group_actor(object) != nil do if Ownable.group_actor(object) != nil do
case apply(Ownable, action_function, [object]) do case object |> Ownable.permissions() |> get_in([permission]) do
role when role in [:member, :moderator, :administrator] -> role when role in @member_roles ->
activity_actor_is_group_member?(actor, object, role) activity_actor_is_group_member?(actor, object, role)
_ -> _ ->
case action_function do case permission do
:role_needed_to_access -> :access ->
Logger.warn("Actor #{actor_url} can't access #{object.url}") Logger.warn("Actor #{actor_url} can't access #{object.url}")
:role_needed_to_update -> :update ->
Logger.warn("Actor #{actor_url} can't update #{object.url}") Logger.warn("Actor #{actor_url} can't update #{object.url}")
:role_needed_to_delete -> :delete ->
Logger.warn("Actor #{actor_url} can't delete #{object.url}") Logger.warn("Actor #{actor_url} can't delete #{object.url}")
end end

View file

@ -3,7 +3,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Actors do
alias Mobilizon.Actors alias Mobilizon.Actors
alias Mobilizon.Actors.{Actor, Follower, Member} alias Mobilizon.Actors.{Actor, Follower, Member}
alias Mobilizon.Federation.ActivityPub alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityPub.{Audience, Relay} alias Mobilizon.Federation.ActivityPub.{Audience, Permission, Relay}
alias Mobilizon.Federation.ActivityPub.Types.Entity alias Mobilizon.Federation.ActivityPub.Types.Entity
alias Mobilizon.Federation.ActivityStream.Convertible alias Mobilizon.Federation.ActivityStream.Convertible
alias Mobilizon.GraphQL.API.Utils, as: APIUtils alias Mobilizon.GraphQL.API.Utils, as: APIUtils
@ -104,9 +104,14 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Actors do
def group_actor(%Actor{} = actor), do: actor def group_actor(%Actor{} = actor), do: actor
def role_needed_to_access(%Actor{} = _group), do: :member def permissions(%Actor{} = _group) do
def role_needed_to_update(%Actor{} = _group), do: :administrator %Permission{
def role_needed_to_delete(%Actor{} = _group), do: :administrator access: :member,
create: nil,
update: :administrator,
delete: :administrator
}
end
@spec join(Actor.t(), Actor.t(), boolean(), map()) :: {:ok, map(), Member.t()} @spec join(Actor.t(), Actor.t(), boolean(), map()) :: {:ok, map(), Member.t()}
def join(%Actor{type: :Group} = group, %Actor{} = actor, _local, additional) do def join(%Actor{type: :Group} = group, %Actor{} = actor, _local, additional) do

View file

@ -4,7 +4,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Comments do
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Discussions.{Comment, Discussion} alias Mobilizon.Discussions.{Comment, Discussion}
alias Mobilizon.Events.{Event, EventOptions} alias Mobilizon.Events.{Event, EventOptions}
alias Mobilizon.Federation.ActivityPub.Audience alias Mobilizon.Federation.ActivityPub.{Audience, Permission}
alias Mobilizon.Federation.ActivityPub.Types.Entity alias Mobilizon.Federation.ActivityPub.Types.Entity
alias Mobilizon.Federation.ActivityStream.Converter.Utils, as: ConverterUtils alias Mobilizon.Federation.ActivityStream.Converter.Utils, as: ConverterUtils
alias Mobilizon.Federation.ActivityStream.Convertible alias Mobilizon.Federation.ActivityStream.Convertible
@ -104,9 +104,13 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Comments do
def group_actor(_), do: nil def group_actor(_), do: nil
def role_needed_to_access(%Comment{}), do: :member def permissions(%Comment{}),
def role_needed_to_update(%Comment{attributed_to: %Actor{} = _group}), do: :administrator do: %Permission{
def role_needed_to_delete(%Comment{attributed_to_id: _attributed_to_id}), do: :administrator access: :member,
create: :member,
update: :administrator,
delete: :administrator
}
# Prepare and sanitize arguments for comments # Prepare and sanitize arguments for comments
defp prepare_args_for_comment(args) do defp prepare_args_for_comment(args) do

View file

@ -4,7 +4,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Discussions do
alias Mobilizon.{Actors, Discussions} alias Mobilizon.{Actors, Discussions}
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Discussions.{Comment, Discussion} alias Mobilizon.Discussions.{Comment, Discussion}
alias Mobilizon.Federation.ActivityPub.Audience alias Mobilizon.Federation.ActivityPub.{Audience, Permission}
alias Mobilizon.Federation.ActivityPub.Types.Entity alias Mobilizon.Federation.ActivityPub.Types.Entity
alias Mobilizon.Federation.ActivityStream.Convertible alias Mobilizon.Federation.ActivityStream.Convertible
alias Mobilizon.GraphQL.API.Utils, as: APIUtils alias Mobilizon.GraphQL.API.Utils, as: APIUtils
@ -110,9 +110,9 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Discussions do
def group_actor(%Discussion{actor_id: actor_id}), do: Actors.get_actor(actor_id) def group_actor(%Discussion{actor_id: actor_id}), do: Actors.get_actor(actor_id)
def role_needed_to_access(%Discussion{}), do: :member def permissions(%Discussion{}) do
def role_needed_to_update(%Discussion{}), do: :moderator %Permission{access: :member, create: :member, update: :moderator, delete: :moderator}
def role_needed_to_delete(%Discussion{}), do: :moderator end
@spec maybe_publish_graphql_subscription(Discussion.t()) :: :ok @spec maybe_publish_graphql_subscription(Discussion.t()) :: :ok
defp maybe_publish_graphql_subscription(%Discussion{} = discussion) do defp maybe_publish_graphql_subscription(%Discussion{} = discussion) do

View file

@ -17,6 +17,7 @@ alias Mobilizon.Federation.ActivityPub.Types.{
alias Mobilizon.Actors.{Actor, Member} alias Mobilizon.Actors.{Actor, Member}
alias Mobilizon.Events.Event alias Mobilizon.Events.Event
alias Mobilizon.Discussions.{Comment, Discussion} alias Mobilizon.Discussions.{Comment, Discussion}
alias Mobilizon.Federation.ActivityPub.Permission
alias Mobilizon.Posts.Post alias Mobilizon.Posts.Post
alias Mobilizon.Resources.Resource alias Mobilizon.Resources.Resource
alias Mobilizon.Todos.{Todo, TodoList} alias Mobilizon.Todos.{Todo, TodoList}
@ -67,14 +68,8 @@ defprotocol Mobilizon.Federation.ActivityPub.Types.Ownable do
@doc "Returns the actor for the entity" @doc "Returns the actor for the entity"
def actor(entity) def actor(entity)
@spec role_needed_to_access(Entity.t()) :: group_role() @spec permissions(Entity.t()) :: Permission.t()
def role_needed_to_access(entity) def permissions(entity)
@spec role_needed_to_update(Entity.t()) :: group_role()
def role_needed_to_update(entity)
@spec role_needed_to_delete(Entity.t()) :: group_role()
def role_needed_to_delete(entity)
end end
defimpl Managable, for: Event do defimpl Managable, for: Event do
@ -85,9 +80,7 @@ end
defimpl Ownable, for: Event do defimpl Ownable, for: Event do
defdelegate group_actor(entity), to: Events defdelegate group_actor(entity), to: Events
defdelegate actor(entity), to: Events defdelegate actor(entity), to: Events
defdelegate role_needed_to_access(entity), to: Events defdelegate permissions(entity), to: Events
defdelegate role_needed_to_update(entity), to: Events
defdelegate role_needed_to_delete(entity), to: Events
end end
defimpl Managable, for: Comment do defimpl Managable, for: Comment do
@ -98,9 +91,7 @@ end
defimpl Ownable, for: Comment do defimpl Ownable, for: Comment do
defdelegate group_actor(entity), to: Comments defdelegate group_actor(entity), to: Comments
defdelegate actor(entity), to: Comments defdelegate actor(entity), to: Comments
defdelegate role_needed_to_access(entity), to: Comments defdelegate permissions(entity), to: Comments
defdelegate role_needed_to_update(entity), to: Comments
defdelegate role_needed_to_delete(entity), to: Comments
end end
defimpl Managable, for: Post do defimpl Managable, for: Post do
@ -111,9 +102,7 @@ end
defimpl Ownable, for: Post do defimpl Ownable, for: Post do
defdelegate group_actor(entity), to: Posts defdelegate group_actor(entity), to: Posts
defdelegate actor(entity), to: Posts defdelegate actor(entity), to: Posts
defdelegate role_needed_to_access(entity), to: Posts defdelegate permissions(entity), to: Posts
defdelegate role_needed_to_update(entity), to: Posts
defdelegate role_needed_to_delete(entity), to: Posts
end end
defimpl Managable, for: Actor do defimpl Managable, for: Actor do
@ -124,9 +113,7 @@ end
defimpl Ownable, for: Actor do defimpl Ownable, for: Actor do
defdelegate group_actor(entity), to: Actors defdelegate group_actor(entity), to: Actors
defdelegate actor(entity), to: Actors defdelegate actor(entity), to: Actors
defdelegate role_needed_to_access(entity), to: Actors defdelegate permissions(entity), to: Actors
defdelegate role_needed_to_update(entity), to: Actors
defdelegate role_needed_to_delete(entity), to: Actors
end end
defimpl Managable, for: TodoList do defimpl Managable, for: TodoList do
@ -137,9 +124,7 @@ end
defimpl Ownable, for: TodoList do defimpl Ownable, for: TodoList do
defdelegate group_actor(entity), to: TodoLists defdelegate group_actor(entity), to: TodoLists
defdelegate actor(entity), to: TodoLists defdelegate actor(entity), to: TodoLists
defdelegate role_needed_to_access(entity), to: TodoLists defdelegate permissions(entity), to: TodoLists
defdelegate role_needed_to_update(entity), to: TodoLists
defdelegate role_needed_to_delete(entity), to: TodoLists
end end
defimpl Managable, for: Todo do defimpl Managable, for: Todo do
@ -150,9 +135,7 @@ end
defimpl Ownable, for: Todo do defimpl Ownable, for: Todo do
defdelegate group_actor(entity), to: Todos defdelegate group_actor(entity), to: Todos
defdelegate actor(entity), to: Todos defdelegate actor(entity), to: Todos
defdelegate role_needed_to_access(entity), to: Todos defdelegate permissions(entity), to: Todos
defdelegate role_needed_to_update(entity), to: Todos
defdelegate role_needed_to_delete(entity), to: Todos
end end
defimpl Managable, for: Resource do defimpl Managable, for: Resource do
@ -163,9 +146,7 @@ end
defimpl Ownable, for: Resource do defimpl Ownable, for: Resource do
defdelegate group_actor(entity), to: Resources defdelegate group_actor(entity), to: Resources
defdelegate actor(entity), to: Resources defdelegate actor(entity), to: Resources
defdelegate role_needed_to_access(entity), to: Resources defdelegate permissions(entity), to: Resources
defdelegate role_needed_to_update(entity), to: Resources
defdelegate role_needed_to_delete(entity), to: Resources
end end
defimpl Managable, for: Discussion do defimpl Managable, for: Discussion do
@ -176,17 +157,13 @@ end
defimpl Ownable, for: Discussion do defimpl Ownable, for: Discussion do
defdelegate group_actor(entity), to: Discussions defdelegate group_actor(entity), to: Discussions
defdelegate actor(entity), to: Discussions defdelegate actor(entity), to: Discussions
defdelegate role_needed_to_access(entity), to: Discussions defdelegate permissions(entity), to: Discussions
defdelegate role_needed_to_update(entity), to: Discussions
defdelegate role_needed_to_delete(entity), to: Discussions
end end
defimpl Ownable, for: Tombstone do defimpl Ownable, for: Tombstone do
defdelegate group_actor(entity), to: Tombstones defdelegate group_actor(entity), to: Tombstones
defdelegate actor(entity), to: Tombstones defdelegate actor(entity), to: Tombstones
defdelegate role_needed_to_access(entity), to: Tombstones defdelegate permissions(entity), to: Tombstones
defdelegate role_needed_to_update(entity), to: Tombstones
defdelegate role_needed_to_delete(entity), to: Tombstones
end end
defimpl Managable, for: Member do defimpl Managable, for: Member do

View file

@ -5,7 +5,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Events do
alias Mobilizon.Events, as: EventsManager alias Mobilizon.Events, as: EventsManager
alias Mobilizon.Events.{Event, Participant} alias Mobilizon.Events.{Event, Participant}
alias Mobilizon.Federation.ActivityPub alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityPub.Audience alias Mobilizon.Federation.ActivityPub.{Audience, Permission}
alias Mobilizon.Federation.ActivityPub.Types.Entity alias Mobilizon.Federation.ActivityPub.Types.Entity
alias Mobilizon.Federation.ActivityStream.Converter.Utils, as: ConverterUtils alias Mobilizon.Federation.ActivityStream.Converter.Utils, as: ConverterUtils
alias Mobilizon.Federation.ActivityStream.Convertible alias Mobilizon.Federation.ActivityStream.Convertible
@ -95,11 +95,14 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Events do
def group_actor(_), do: nil def group_actor(_), do: nil
def role_needed_to_access(%Event{draft: false}), do: :member def permissions(%Event{draft: draft, attributed_to_id: _attributed_to_id}) do
def role_needed_to_access(%Event{}), do: :moderator %Permission{
def role_needed_to_update(%Event{attributed_to: %Actor{} = _group}), do: :moderator access: if(draft, do: nil, else: :member),
def role_needed_to_delete(%Event{attributed_to_id: _attributed_to_id}), do: :moderator create: :moderator,
def role_needed_to_delete(_), do: nil update: :moderator,
delete: :moderator
}
end
def join(%Event{} = event, %Actor{} = actor, _local, additional) do def join(%Event{} = event, %Actor{} = actor, _local, additional) do
with {:maximum_attendee_capacity, true} <- with {:maximum_attendee_capacity, true} <-

View file

@ -2,7 +2,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Posts do
@moduledoc false @moduledoc false
alias Mobilizon.{Actors, Posts, Tombstone} alias Mobilizon.{Actors, Posts, Tombstone}
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Federation.ActivityPub.Audience alias Mobilizon.Federation.ActivityPub.{Audience, Permission}
alias Mobilizon.Federation.ActivityPub.Types.Entity alias Mobilizon.Federation.ActivityPub.Types.Entity
alias Mobilizon.Federation.ActivityStream.Converter.Utils, as: ConverterUtils alias Mobilizon.Federation.ActivityStream.Converter.Utils, as: ConverterUtils
alias Mobilizon.Federation.ActivityStream.Convertible alias Mobilizon.Federation.ActivityStream.Convertible
@ -91,8 +91,12 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Posts do
def group_actor(%Post{attributed_to_id: attributed_to_id}), def group_actor(%Post{attributed_to_id: attributed_to_id}),
do: Actors.get_actor(attributed_to_id) do: Actors.get_actor(attributed_to_id)
def role_needed_to_access(%Post{draft: false}), do: :member def permissions(%Post{draft: draft}) do
def role_needed_to_access(%Post{}), do: :moderator %Permission{
def role_needed_to_update(%Post{}), do: :moderator access: if(draft, do: nil, else: :member),
def role_needed_to_delete(%Post{}), do: :moderator create: :moderator,
update: :moderator,
delete: :moderator
}
end
end end

View file

@ -2,6 +2,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Resources do
@moduledoc false @moduledoc false
alias Mobilizon.{Actors, Resources} alias Mobilizon.{Actors, Resources}
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Federation.ActivityPub.Permission
alias Mobilizon.Federation.ActivityPub.Types.Entity alias Mobilizon.Federation.ActivityPub.Types.Entity
alias Mobilizon.Federation.ActivityStream.Convertible alias Mobilizon.Federation.ActivityStream.Convertible
alias Mobilizon.Resources.Resource alias Mobilizon.Resources.Resource
@ -170,7 +171,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Resources do
def group_actor(%Resource{actor_id: actor_id}), do: Actors.get_actor(actor_id) def group_actor(%Resource{actor_id: actor_id}), do: Actors.get_actor(actor_id)
def role_needed_to_access(%Resource{}), do: :member def permissions(%Resource{}) do
def role_needed_to_update(%Resource{}), do: :member %Permission{access: :member, create: :member, update: :member, delete: :member}
def role_needed_to_delete(%Resource{}), do: :member end
end end

View file

@ -2,6 +2,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.TodoLists do
@moduledoc false @moduledoc false
alias Mobilizon.{Actors, Todos} alias Mobilizon.{Actors, Todos}
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Federation.ActivityPub.Permission
alias Mobilizon.Federation.ActivityPub.Types.Entity alias Mobilizon.Federation.ActivityPub.Types.Entity
alias Mobilizon.Federation.ActivityStream alias Mobilizon.Federation.ActivityStream
alias Mobilizon.Federation.ActivityStream.Convertible alias Mobilizon.Federation.ActivityStream.Convertible
@ -68,7 +69,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.TodoLists do
def group_actor(%TodoList{actor_id: actor_id}), do: Actors.get_actor(actor_id) def group_actor(%TodoList{actor_id: actor_id}), do: Actors.get_actor(actor_id)
def role_needed_to_access(%TodoList{}), do: :member def permissions(%TodoList{}) do
def role_needed_to_update(%TodoList{}), do: :member %Permission{access: :member, create: :member, update: :member, delete: :member}
def role_needed_to_delete(%TodoList{}), do: :member end
end end

View file

@ -2,6 +2,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Todos do
@moduledoc false @moduledoc false
alias Mobilizon.{Actors, Todos} alias Mobilizon.{Actors, Todos}
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Federation.ActivityPub.Permission
alias Mobilizon.Federation.ActivityPub.Types.Entity alias Mobilizon.Federation.ActivityPub.Types.Entity
alias Mobilizon.Federation.ActivityStream.Convertible alias Mobilizon.Federation.ActivityStream.Convertible
alias Mobilizon.Todos.{Todo, TodoList} alias Mobilizon.Todos.{Todo, TodoList}
@ -80,7 +81,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Todos do
end end
end end
def role_needed_to_access(%Todo{}), do: :member def permissions(%Todo{}) do
def role_needed_to_update(%Todo{}), do: :member %Permission{access: :member, create: :member, update: :member, delete: :member}
def role_needed_to_delete(%Todo{}), do: :member end
end end

View file

@ -2,6 +2,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Tombstones do
@moduledoc false @moduledoc false
alias Mobilizon.{Actors, Tombstone} alias Mobilizon.{Actors, Tombstone}
alias Mobilizon.Actors.Actor alias Mobilizon.Actors.Actor
alias Mobilizon.Federation.ActivityPub.Permission
def actor(%Tombstone{actor: %Actor{id: actor_id}}), do: Actors.get_actor(actor_id) def actor(%Tombstone{actor: %Actor{id: actor_id}}), do: Actors.get_actor(actor_id)
@ -12,7 +13,7 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Tombstones do
def group_actor(_), do: nil def group_actor(_), do: nil
def role_needed_to_access(%Actor{}), do: nil def permissions(_) do
def role_needed_to_update(%Actor{}), do: nil %Permission{access: nil, create: nil, update: nil, delete: nil}
def role_needed_to_delete(%Actor{}), do: nil end
end end