X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fuser.ex;h=a52e536d3397f1e96e43ae46fb4b31128894e981;hb=943324b66158d1bd2449894ec41bc544c56058a7;hp=4e227b08d715f6aac0db9bcd61ac8163059b7ebb;hpb=968d7490b689ba501a64f350841dc8f9b33b5244;p=akkoma diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 4e227b08d..a52e536d3 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# 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,28 @@ defmodule Pleroma.User do timestamps() end - def auth_active?(user), do: user.info && !user.info.confirmation_pending + def auth_active?(%User{local: false}), do: true + + def auth_active?(%User{info: %User.Info{confirmation_pending: false}}), do: true + + def auth_active?(%User{info: %User.Info{confirmation_pending: true}}), + do: !Pleroma.Config.get([:instance, :account_activation_required]) + + def auth_active?(_), do: false + + def visible_for?(user, for_user \\ nil) + + def visible_for?(%User{id: user_id}, %User{id: for_id}) when user_id == for_id, do: true + + def visible_for?(%User{} = user, for_user) do + auth_active?(user) || superuser?(for_user) + end + + def visible_for?(_, _), do: false + + def superuser?(%User{local: true, info: %User.Info{is_admin: true}}), do: true + def superuser?(%User{local: true, info: %User.Info{is_moderator: true}}), do: true + def superuser?(_), do: false def avatar_url(user) do case user.avatar do @@ -74,15 +101,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 +206,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 +215,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]) @@ -208,16 +237,31 @@ defmodule Pleroma.User do end end + defp autofollow_users(user) do + candidates = Pleroma.Config.get([:instance, :autofollowed_nicknames]) + + autofollowed_users = + from(u in User, + where: u.local == true, + where: u.nickname in ^candidates + ) + |> Repo.all() + + follow_all(user, autofollowed_users) + end + @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), + {:ok, user} <- autofollow_users(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() @@ -260,6 +304,25 @@ defmodule Pleroma.User do end end + @doc "A mass follow for local users. Ignores blocks and has no side effects" + @spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()} + def follow_all(follower, followeds) do + following = + (follower.following ++ Enum.map(followeds, fn %{follower_address: fa} -> fa end)) + |> Enum.uniq() + + {:ok, follower} = + follower + |> follow_changeset(%{following: following}) + |> update_and_set_cache + + Enum.each(followeds, fn followed -> + update_follower_count(followed) + end) + + {:ok, follower} + end + def follow(%User{} = follower, %User{info: info} = followed) do user_config = Application.get_env(:pleroma, :user) deny_follow_blocked = Keyword.get(user_config, :deny_follow_blocked) @@ -319,6 +382,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 @@ -327,6 +408,15 @@ defmodule Pleroma.User do Repo.get_by(User, ap_id: ap_id) end + # This is mostly an SPC migration fix. This guesses the user nickname (by taking the last part of the ap_id and the domain) and tries to get that user + def get_by_guessed_nickname(ap_id) do + domain = URI.parse(ap_id).host + name = List.last(String.split(ap_id, "/")) + nickname = "#{name}@#{domain}" + + get_by_nickname(nickname) + end + def update_and_set_cache(changeset) do with {:ok, user} <- Repo.update(changeset) do Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user) @@ -355,7 +445,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,11 +487,7 @@ 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 + def get_followers_query(%User{id: id, follower_address: follower_address}, nil) do from( u in User, where: fragment("? <@ ?", ^[follower_address], u.following), @@ -405,13 +495,23 @@ defmodule Pleroma.User do ) end - def get_followers(user) do - q = get_followers_query(user) + def get_followers_query(user, page) do + from( + u in get_followers_query(user, nil), + limit: 20, + offset: ^((page - 1) * 20) + ) + end + + def get_followers_query(user), do: get_followers_query(user, nil) + + def get_followers(user, page \\ nil) do + q = get_followers_query(user, page) {:ok, Repo.all(q)} end - def get_friends_query(%User{id: id, following: following}) do + def get_friends_query(%User{id: id, following: following}, nil) do from( u in User, where: u.follower_address in ^following, @@ -419,8 +519,18 @@ defmodule Pleroma.User do ) end - def get_friends(user) do - q = get_friends_query(user) + def get_friends_query(user, page) do + from( + u in get_friends_query(user, nil), + limit: 20, + offset: ^((page - 1) * 20) + ) + end + + def get_friends_query(user), do: get_friends_query(user, nil) + + def get_friends(user, page \\ nil) do + q = get_friends_query(user, page) {:ok, Repo.all(q)} end @@ -455,6 +565,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} @@ -569,7 +680,7 @@ defmodule Pleroma.User do select_merge: %{ search_distance: fragment( - "? <-> (? || ?)", + "? <-> (? || coalesce(?, ''))", ^query, u.nickname, u.name @@ -588,6 +699,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 +769,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 +857,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) @@ -907,4 +1040,14 @@ defmodule Pleroma.User do @strict_local_nickname_regex end end + + def error_user(ap_id) do + %User{ + name: ap_id, + ap_id: ap_id, + info: %User.Info{}, + nickname: "erroruser@example.com", + inserted_at: NaiveDateTime.utc_now() + } + end end