Merge branch 'activitypub-c2s-delete-activity' into 'develop'
[akkoma] / lib / pleroma / user.ex
index 4e227b08d715f6aac0db9bcd61ac8163059b7ebb..1edded4151fe0e4a16a9635acec473c04c77c6a8 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.User do
   use Ecto.Schema
 
@@ -9,6 +13,8 @@ defmodule Pleroma.User do
   alias Pleroma.Web.{OStatus, Websub, OAuth}
   alias Pleroma.Web.ActivityPub.{Utils, ActivityPub}
 
+  require Logger
+
   @type t :: %__MODULE__{}
 
   @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])?)*$/
@@ -38,7 +44,20 @@ defmodule Pleroma.User do
     timestamps()
   end
 
-  def auth_active?(user), do: user.info && !user.info.confirmation_pending
+  def auth_active?(%User{} = user) do
+    (user.info && !user.info.confirmation_pending) ||
+      !Pleroma.Config.get([:instance, :account_activation_required])
+  end
+
+  def remote_or_auth_active?(%User{} = user), do: !user.local || auth_active?(user)
+
+  def visible_for?(%User{} = user, for_user \\ nil) do
+    User.remote_or_auth_active?(user) || (for_user && for_user.id == user.id) ||
+      User.superuser?(for_user)
+  end
+
+  def superuser?(nil), do: false
+  def superuser?(%User{} = user), do: user.info && User.Info.superuser?(user.info)
 
   def avatar_url(user) do
     case user.avatar do
@@ -74,15 +93,14 @@ defmodule Pleroma.User do
 
   def user_info(%User{} = user) do
     oneself = if user.local, do: 1, else: 0
-    user_info = user.info
 
     %{
       following_count: length(user.following) - oneself,
-      note_count: user_info.note_count,
-      follower_count: user_info.follower_count,
-      locked: user_info.locked,
-      confirmation_pending: user_info.confirmation_pending,
-      default_scope: user_info.default_scope
+      note_count: user.info.note_count,
+      follower_count: user.info.follower_count,
+      locked: user.info.locked,
+      confirmation_pending: user.info.confirmation_pending,
+      default_scope: user.info.default_scope
     }
   end
 
@@ -180,6 +198,8 @@ defmodule Pleroma.User do
         :unconfirmed
       end
 
+    info_change = User.Info.confirmation_changeset(%User.Info{}, confirmation_status)
+
     changeset =
       struct
       |> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation])
@@ -187,11 +207,12 @@ defmodule Pleroma.User do
       |> validate_confirmation(:password)
       |> unique_constraint(:email)
       |> unique_constraint(:nickname)
+      |> validate_exclusion(:nickname, Pleroma.Config.get([Pleroma.User, :restricted_nicknames]))
       |> validate_format(:nickname, local_nickname_regex())
       |> validate_format(:email, @email_regex)
       |> validate_length(:bio, max: 1000)
       |> validate_length(:name, min: 1, max: 100)
-      |> put_change(:info, User.Info.confirmation_changeset(%User.Info{}, confirmation_status))
+      |> put_change(:info, info_change)
 
     if changeset.valid? do
       hashed = Pbkdf2.hashpwsalt(changeset.changes[:password])
@@ -210,14 +231,15 @@ defmodule Pleroma.User do
 
   @doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
   def register(%Ecto.Changeset{} = changeset) do
-    with {:ok, user} <- Repo.insert(changeset) do
-      {:ok, _} = try_send_confirmation_email(user)
+    with {:ok, user} <- Repo.insert(changeset),
+         {:ok, _} = try_send_confirmation_email(user) do
       {:ok, user}
     end
   end
 
   def try_send_confirmation_email(%User{} = user) do
-    if user.info.confirmation_pending do
+    if user.info.confirmation_pending &&
+         Pleroma.Config.get([:instance, :account_activation_required]) do
       user
       |> Pleroma.UserEmail.account_confirmation_email()
       |> Pleroma.Mailer.deliver()
@@ -319,6 +341,24 @@ defmodule Pleroma.User do
     Enum.member?(follower.following, followed.follower_address)
   end
 
+  def follow_import(%User{} = follower, followed_identifiers)
+      when is_list(followed_identifiers) do
+    Enum.map(
+      followed_identifiers,
+      fn followed_identifier ->
+        with %User{} = followed <- get_or_fetch(followed_identifier),
+             {:ok, follower} <- maybe_direct_follow(follower, followed),
+             {:ok, _} <- ActivityPub.follow(follower, followed) do
+          followed
+        else
+          err ->
+            Logger.debug("follow_import failed for #{followed_identifier} with: #{inspect(err)}")
+            err
+        end
+      end
+    )
+  end
+
   def locked?(%User{} = user) do
     user.info.locked || false
   end
@@ -355,7 +395,11 @@ defmodule Pleroma.User do
   end
 
   def get_by_nickname(nickname) do
-    Repo.get_by(User, nickname: nickname)
+    Repo.get_by(User, nickname: nickname) ||
+      if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()})i, nickname) do
+        [local_nickname, _] = String.split(nickname, "@")
+        Repo.get_by(User, nickname: local_nickname)
+      end
   end
 
   def get_by_nickname_or_email(nickname_or_email) do
@@ -393,10 +437,6 @@ defmodule Pleroma.User do
     end
   end
 
-  def get_by_confirmation_token(token) do
-    Repo.one(from(u in User, where: fragment("? ->> 'confirmation_token' = ?", u.info, ^token)))
-  end
-
   def get_followers_query(%User{id: id, follower_address: follower_address}) do
     from(
       u in User,
@@ -455,6 +495,7 @@ defmodule Pleroma.User do
       Enum.map(reqs, fn req -> req.actor end)
       |> Enum.uniq()
       |> Enum.map(fn ap_id -> get_by_ap_id(ap_id) end)
+      |> Enum.filter(fn u -> !is_nil(u) end)
       |> Enum.filter(fn u -> !following?(u, user) end)
 
     {:ok, users}
@@ -588,6 +629,23 @@ defmodule Pleroma.User do
     Repo.all(q)
   end
 
+  def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_identifiers) do
+    Enum.map(
+      blocked_identifiers,
+      fn blocked_identifier ->
+        with %User{} = blocked <- get_or_fetch(blocked_identifier),
+             {:ok, blocker} <- block(blocker, blocked),
+             {:ok, _} <- ActivityPub.block(blocker, blocked) do
+          blocked
+        else
+          err ->
+            Logger.debug("blocks_import failed for #{blocked_identifier} with: #{inspect(err)}")
+            err
+        end
+      end
+    )
+  end
+
   def block(blocker, %User{ap_id: ap_id} = blocked) do
     # sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213)
     blocker =
@@ -641,6 +699,9 @@ defmodule Pleroma.User do
       end)
   end
 
+  def blocked_users(user),
+    do: Repo.all(from(u in User, where: u.ap_id in ^user.info.blocks))
+
   def block_domain(user, domain) do
     info_cng =
       user.info
@@ -726,7 +787,9 @@ defmodule Pleroma.User do
     Pleroma.HTML.Scrubber.TwitterText
   end
 
-  def html_filter_policy(_), do: nil
+  @default_scrubbers Pleroma.Config.get([:markup, :scrub_policy])
+
+  def html_filter_policy(_), do: @default_scrubbers
 
   def get_or_fetch_by_ap_id(ap_id) do
     user = get_by_ap_id(ap_id)