Validate alias IDs
[akkoma] / lib / pleroma / user.ex
index b9989f9018194122f2499918afedf2f88d73ad4f..66664235b09c29d6190b2cd9cfcbf12ca7e92865 100644 (file)
@@ -47,6 +47,8 @@ defmodule Pleroma.User do
 
   # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
   @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])?)*$/
+  # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
+  @url_regex ~r/https?:\/\/[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&\/=]*)/
 
   @strict_local_nickname_regex ~r/^[a-zA-Z\d]+$/
   @extended_local_nickname_regex ~r/^[a-zA-Z\d_-]+$/
@@ -89,6 +91,7 @@ defmodule Pleroma.User do
     field(:keys, :string)
     field(:public_key, :string)
     field(:ap_id, :string)
+    field(:ap_aliases, {:array, :string}, default: [])
     field(:avatar, :map, default: %{})
     field(:local, :boolean, default: true)
     field(:follower_address, :string)
@@ -530,11 +533,21 @@ defmodule Pleroma.User do
   end
 
   defp put_emoji(changeset) do
-    bio = get_change(changeset, :bio)
-    name = get_change(changeset, :name)
+    emojified_fields = [:bio, :name, :raw_fields]
+
+    if Enum.any?(changeset.changes, fn {k, _} -> k in emojified_fields end) do
+      bio = Emoji.Formatter.get_emoji_map(get_field(changeset, :bio))
+      name = Emoji.Formatter.get_emoji_map(get_field(changeset, :name))
+
+      emoji = Map.merge(bio, name)
+
+      emoji =
+        changeset
+        |> get_field(:raw_fields)
+        |> Enum.reduce(emoji, fn x, acc ->
+          Map.merge(acc, Emoji.Formatter.get_emoji_map(x["name"] <> x["value"]))
+        end)
 
-    if bio || name do
-      emoji = Map.merge(Emoji.Formatter.get_emoji_map(bio), Emoji.Formatter.get_emoji_map(name))
       put_change(changeset, :emoji, emoji)
     else
       changeset
@@ -2258,4 +2271,38 @@ defmodule Pleroma.User do
     |> Map.put(:bio, HTML.filter_tags(user.bio, filter))
     |> Map.put(:fields, fields)
   end
+
+  def add_aliases(%User{} = user, aliases) when is_list(aliases) do
+    alias_set =
+      (user.ap_aliases ++ aliases)
+      |> MapSet.new()
+      |> MapSet.to_list()
+
+    user
+    |> change(%{ap_aliases: alias_set})
+    |> validate_ap_aliases()
+    |> Repo.update()
+  end
+
+  def delete_aliases(%User{} = user, aliases) when is_list(aliases) do
+    alias_set =
+      user.ap_aliases
+      |> MapSet.new()
+      |> MapSet.difference(MapSet.new(aliases))
+      |> MapSet.to_list()
+
+    user
+    |> change(%{ap_aliases: alias_set})
+    |> validate_ap_aliases()
+    |> Repo.update()
+  end
+
+  defp validate_ap_aliases(changeset) do
+    validate_change(changeset, :ap_aliases, fn :ap_aliases, ap_aliases ->
+      case Enum.all?(ap_aliases, fn a -> Regex.match?(@url_regex, a) end) do
+        true -> []
+        false -> [ap_aliases: "Invalid ap_id format. Must be a URL."]
+      end
+    end)
+  end
 end