2018-05-18 09:56:21 +02:00
|
|
|
import EctoEnum
|
|
|
|
|
2018-07-27 10:45:35 +02:00
|
|
|
defenum(Eventos.Actors.ActorTypeEnum, :actor_type, [
|
|
|
|
:Person,
|
|
|
|
:Application,
|
|
|
|
:Group,
|
|
|
|
:Organization,
|
|
|
|
:Service
|
|
|
|
])
|
2018-05-18 09:56:21 +02:00
|
|
|
|
|
|
|
defmodule Eventos.Actors.Actor do
|
|
|
|
@moduledoc """
|
|
|
|
Represents an actor (local and remote actors)
|
|
|
|
"""
|
|
|
|
use Ecto.Schema
|
|
|
|
import Ecto.Changeset
|
|
|
|
alias Eventos.Actors
|
|
|
|
alias Eventos.Actors.{Actor, User, Follower, Member}
|
|
|
|
alias Eventos.Events.Event
|
|
|
|
alias Eventos.Service.ActivityPub
|
|
|
|
|
|
|
|
import Ecto.Query
|
|
|
|
alias Eventos.Repo
|
|
|
|
|
|
|
|
import Logger
|
|
|
|
|
2018-07-27 10:45:35 +02:00
|
|
|
# @type t :: %Actor{description: String.t, id: integer(), inserted_at: DateTime.t, updated_at: DateTime.t, display_name: String.t, domain: String.t, keys: String.t, suspended: boolean(), url: String.t, username: String.t, organized_events: list(), groups: list(), group_request: list(), user: User.t, field: ActorTypeEnum.t}
|
2018-05-18 09:56:21 +02:00
|
|
|
|
|
|
|
schema "actors" do
|
2018-07-27 10:45:35 +02:00
|
|
|
field(:url, :string)
|
|
|
|
field(:outbox_url, :string)
|
|
|
|
field(:inbox_url, :string)
|
|
|
|
field(:following_url, :string)
|
|
|
|
field(:followers_url, :string)
|
|
|
|
field(:shared_inbox_url, :string)
|
|
|
|
field(:type, Eventos.Actors.ActorTypeEnum, default: :Person)
|
|
|
|
field(:name, :string)
|
|
|
|
field(:domain, :string)
|
|
|
|
field(:summary, :string)
|
|
|
|
field(:preferred_username, :string)
|
|
|
|
field(:keys, :string)
|
|
|
|
field(:manually_approves_followers, :boolean, default: false)
|
|
|
|
field(:suspended, :boolean, default: false)
|
|
|
|
field(:avatar_url, :string)
|
|
|
|
field(:banner_url, :string)
|
|
|
|
many_to_many(:followers, Actor, join_through: Follower)
|
|
|
|
has_many(:organized_events, Event, foreign_key: :organizer_actor_id)
|
|
|
|
many_to_many(:memberships, Actor, join_through: Member)
|
|
|
|
belongs_to(:user, User)
|
2018-05-18 09:56:21 +02:00
|
|
|
|
|
|
|
timestamps()
|
|
|
|
end
|
|
|
|
|
|
|
|
@doc false
|
|
|
|
def changeset(%Actor{} = actor, attrs) do
|
|
|
|
actor
|
2018-07-27 10:45:35 +02:00
|
|
|
|> Ecto.Changeset.cast(attrs, [
|
|
|
|
:url,
|
|
|
|
:outbox_url,
|
|
|
|
:inbox_url,
|
|
|
|
:shared_inbox_url,
|
|
|
|
:following_url,
|
|
|
|
:followers_url,
|
|
|
|
:type,
|
|
|
|
:name,
|
|
|
|
:domain,
|
|
|
|
:summary,
|
|
|
|
:preferred_username,
|
|
|
|
:keys,
|
|
|
|
:manually_approves_followers,
|
|
|
|
:suspended,
|
|
|
|
:avatar_url,
|
|
|
|
:banner_url,
|
|
|
|
:user_id
|
|
|
|
])
|
2018-07-10 14:56:10 +02:00
|
|
|
|> put_change(:url, "#{EventosWeb.Endpoint.url()}/@#{attrs["prefered_username"]}")
|
2018-06-14 17:25:55 +02:00
|
|
|
|> validate_required([:preferred_username, :keys, :suspended, :url])
|
2018-05-30 14:27:21 +02:00
|
|
|
|> unique_constraint(:prefered_username, name: :actors_preferred_username_domain_index)
|
2018-05-18 09:56:21 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def registration_changeset(%Actor{} = actor, attrs) do
|
|
|
|
actor
|
2018-07-27 10:45:35 +02:00
|
|
|
|> Ecto.Changeset.cast(attrs, [
|
|
|
|
:preferred_username,
|
|
|
|
:domain,
|
|
|
|
:name,
|
|
|
|
:summary,
|
|
|
|
:keys,
|
|
|
|
:keys,
|
|
|
|
:suspended,
|
|
|
|
:url,
|
|
|
|
:type,
|
|
|
|
:avatar_url,
|
|
|
|
:user_id
|
|
|
|
])
|
2018-06-14 17:25:55 +02:00
|
|
|
|> unique_constraint(:preferred_username, name: :actors_preferred_username_domain_index)
|
|
|
|
|> put_change(:url, "#{EventosWeb.Endpoint.url()}/@#{attrs["prefered_username"]}")
|
|
|
|
|> validate_required([:preferred_username, :keys, :suspended, :url, :type])
|
2018-05-18 09:56:21 +02:00
|
|
|
end
|
|
|
|
|
2018-08-01 14:45:18 +02:00
|
|
|
# TODO : Use me !
|
2018-05-18 09:56:21 +02:00
|
|
|
@email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
|
|
|
|
def remote_actor_creation(params) do
|
|
|
|
changes =
|
|
|
|
%Actor{}
|
2018-07-27 10:45:35 +02:00
|
|
|
|> Ecto.Changeset.cast(params, [
|
|
|
|
:url,
|
|
|
|
:outbox_url,
|
|
|
|
:inbox_url,
|
|
|
|
:shared_inbox_url,
|
|
|
|
:following_url,
|
|
|
|
:followers_url,
|
|
|
|
:type,
|
|
|
|
:name,
|
|
|
|
:domain,
|
|
|
|
:summary,
|
|
|
|
:preferred_username,
|
|
|
|
:keys,
|
|
|
|
:manually_approves_followers,
|
|
|
|
:avatar_url,
|
|
|
|
:banner_url
|
|
|
|
])
|
|
|
|
|> validate_required([
|
|
|
|
:url,
|
|
|
|
:outbox_url,
|
|
|
|
:inbox_url,
|
|
|
|
:type,
|
|
|
|
:name,
|
|
|
|
:domain,
|
|
|
|
:preferred_username,
|
|
|
|
:keys
|
|
|
|
])
|
2018-05-18 09:56:21 +02:00
|
|
|
|> unique_constraint(:preferred_username, name: :actors_preferred_username_domain_index)
|
|
|
|
|> validate_length(:summary, max: 5000)
|
|
|
|
|> validate_length(:preferred_username, max: 100)
|
|
|
|
|> put_change(:local, false)
|
|
|
|
|
|
|
|
Logger.debug("Remote actor creation")
|
2018-07-27 10:45:35 +02:00
|
|
|
Logger.debug(inspect(changes))
|
2018-05-18 09:56:21 +02:00
|
|
|
changes
|
|
|
|
end
|
|
|
|
|
2018-05-30 18:59:13 +02:00
|
|
|
def group_creation(%Actor{} = actor, params) do
|
|
|
|
actor
|
2018-07-27 10:45:35 +02:00
|
|
|
|> Ecto.Changeset.cast(params, [
|
|
|
|
:url,
|
|
|
|
:outbox_url,
|
|
|
|
:inbox_url,
|
|
|
|
:shared_inbox_url,
|
|
|
|
:type,
|
|
|
|
:name,
|
|
|
|
:domain,
|
|
|
|
:summary,
|
|
|
|
:preferred_username,
|
|
|
|
:avatar_url,
|
|
|
|
:banner_url
|
|
|
|
])
|
|
|
|
|> put_change(
|
|
|
|
:outbox_url,
|
|
|
|
"#{EventosWeb.Endpoint.url()}/@#{params["prefered_username"]}/outbox"
|
|
|
|
)
|
|
|
|
|> put_change(
|
|
|
|
:inbox_url,
|
|
|
|
"#{EventosWeb.Endpoint.url()}/@#{params["prefered_username"]}/inbox"
|
|
|
|
)
|
2018-05-30 18:59:13 +02:00
|
|
|
|> put_change(:shared_inbox_url, "#{EventosWeb.Endpoint.url()}/inbox")
|
|
|
|
|> put_change(:url, "#{EventosWeb.Endpoint.url()}/@#{params["prefered_username"]}")
|
|
|
|
|> put_change(:domain, nil)
|
2018-07-27 10:45:35 +02:00
|
|
|
|> put_change(:type, :Group)
|
2018-05-30 18:59:13 +02:00
|
|
|
|> validate_required([:url, :outbox_url, :inbox_url, :type, :name, :preferred_username])
|
|
|
|
|> validate_length(:summary, max: 5000)
|
|
|
|
|> validate_length(:preferred_username, max: 100)
|
|
|
|
|> put_change(:local, true)
|
|
|
|
end
|
|
|
|
|
2018-05-18 09:56:21 +02:00
|
|
|
def get_or_fetch_by_url(url) do
|
|
|
|
if user = Actors.get_actor_by_url(url) do
|
|
|
|
user
|
|
|
|
else
|
|
|
|
case ActivityPub.make_actor_from_url(url) do
|
|
|
|
{:ok, user} ->
|
|
|
|
user
|
2018-07-27 10:45:35 +02:00
|
|
|
|
|
|
|
_ ->
|
|
|
|
{:error, "Could not fetch by AP id"}
|
2018-05-18 09:56:21 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-08-01 14:45:18 +02:00
|
|
|
@spec get_public_key_for_url(String.t()) :: {:ok, String.t()}
|
2018-05-18 09:56:21 +02:00
|
|
|
def get_public_key_for_url(url) do
|
|
|
|
with %Actor{} = actor <- get_or_fetch_by_url(url) do
|
2018-08-01 14:45:18 +02:00
|
|
|
actor.keys
|
2018-07-27 10:45:35 +02:00
|
|
|
|> Eventos.Service.ActivityPub.Utils.pem_to_public_key()
|
2018-05-18 09:56:21 +02:00
|
|
|
else
|
|
|
|
_ -> :error
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-08-03 10:16:22 +02:00
|
|
|
@doc """
|
|
|
|
Get followers from an actor
|
|
|
|
|
|
|
|
If actor A and C both follow actor B, actor B's followers are A and C
|
|
|
|
"""
|
2018-08-01 14:45:18 +02:00
|
|
|
def get_followers(%Actor{id: actor_id} = _actor) do
|
2018-05-18 09:56:21 +02:00
|
|
|
Repo.all(
|
2018-07-27 10:45:35 +02:00
|
|
|
from(
|
|
|
|
a in Actor,
|
|
|
|
join: f in Follower,
|
|
|
|
on: a.id == f.actor_id,
|
|
|
|
where: f.target_actor_id == ^actor_id
|
|
|
|
)
|
2018-05-18 09:56:21 +02:00
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2018-08-03 10:16:22 +02:00
|
|
|
@doc """
|
|
|
|
Get followings from an actor
|
|
|
|
|
|
|
|
If actor A follows actor B and C, actor A's followings are B and B
|
|
|
|
"""
|
2018-08-01 14:45:18 +02:00
|
|
|
def get_followings(%Actor{id: actor_id} = _actor) do
|
2018-05-18 09:56:21 +02:00
|
|
|
Repo.all(
|
2018-07-27 10:45:35 +02:00
|
|
|
from(
|
|
|
|
a in Actor,
|
|
|
|
join: f in Follower,
|
|
|
|
on: a.id == f.target_actor_id,
|
|
|
|
where: f.actor_id == ^actor_id
|
|
|
|
)
|
2018-05-18 09:56:21 +02:00
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|