logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://anongit.hacktivis.me/git/pleroma.git/

user.ex (90054B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.User do
  5. use Ecto.Schema
  6. import Ecto.Changeset
  7. import Ecto.Query
  8. import Ecto, only: [assoc: 2]
  9. import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
  10. alias Ecto.Multi
  11. alias Pleroma.Activity
  12. alias Pleroma.Config
  13. alias Pleroma.Conversation.Participation
  14. alias Pleroma.Delivery
  15. alias Pleroma.EctoType.ActivityPub.ObjectValidators
  16. alias Pleroma.Emoji
  17. alias Pleroma.FollowingRelationship
  18. alias Pleroma.Formatter
  19. alias Pleroma.Hashtag
  20. alias Pleroma.HTML
  21. alias Pleroma.Keys
  22. alias Pleroma.MFA
  23. alias Pleroma.Notification
  24. alias Pleroma.Object
  25. alias Pleroma.Registration
  26. alias Pleroma.Repo
  27. alias Pleroma.User
  28. alias Pleroma.User.HashtagFollow
  29. alias Pleroma.UserRelationship
  30. alias Pleroma.Web.ActivityPub.ActivityPub
  31. alias Pleroma.Web.ActivityPub.Builder
  32. alias Pleroma.Web.ActivityPub.Pipeline
  33. alias Pleroma.Web.ActivityPub.Utils
  34. alias Pleroma.Web.CommonAPI
  35. alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
  36. alias Pleroma.Web.Endpoint
  37. alias Pleroma.Web.OAuth
  38. alias Pleroma.Web.RelMe
  39. alias Pleroma.Workers.BackgroundWorker
  40. alias Pleroma.Workers.DeleteWorker
  41. alias Pleroma.Workers.UserRefreshWorker
  42. require Logger
  43. require Pleroma.Constants
  44. @type t :: %__MODULE__{}
  45. @type account_status ::
  46. :active
  47. | :deactivated
  48. | :password_reset_pending
  49. | :confirmation_pending
  50. | :approval_pending
  51. @primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true}
  52. # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
  53. @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])?)*$/
  54. @strict_local_nickname_regex ~r/^[a-zA-Z\d]+$/
  55. @extended_local_nickname_regex ~r/^[a-zA-Z\d_-]+$/
  56. # AP ID user relationships (blocks, mutes etc.)
  57. # Format: [rel_type: [outgoing_rel: :outgoing_rel_target, incoming_rel: :incoming_rel_source]]
  58. @user_relationships_config [
  59. block: [
  60. blocker_blocks: :blocked_users,
  61. blockee_blocks: :blocker_users
  62. ],
  63. mute: [
  64. muter_mutes: :muted_users,
  65. mutee_mutes: :muter_users
  66. ],
  67. reblog_mute: [
  68. reblog_muter_mutes: :reblog_muted_users,
  69. reblog_mutee_mutes: :reblog_muter_users
  70. ],
  71. notification_mute: [
  72. notification_muter_mutes: :notification_muted_users,
  73. notification_mutee_mutes: :notification_muter_users
  74. ],
  75. # Note: `inverse_subscription` relationship is inverse: subscriber acts as relationship target
  76. inverse_subscription: [
  77. subscribee_subscriptions: :subscriber_users,
  78. subscriber_subscriptions: :subscribee_users
  79. ],
  80. endorsement: [
  81. endorser_endorsements: :endorsed_users,
  82. endorsee_endorsements: :endorser_users
  83. ]
  84. ]
  85. @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
  86. schema "users" do
  87. field(:bio, :string, default: "")
  88. field(:raw_bio, :string)
  89. field(:email, :string)
  90. field(:name, :string)
  91. field(:nickname, :string)
  92. field(:password_hash, :string)
  93. field(:password, :string, virtual: true)
  94. field(:password_confirmation, :string, virtual: true)
  95. field(:keys, :string)
  96. field(:public_key, :string)
  97. field(:ap_id, :string)
  98. field(:avatar, :map, default: %{})
  99. field(:local, :boolean, default: true)
  100. field(:follower_address, :string)
  101. field(:following_address, :string)
  102. field(:featured_address, :string)
  103. field(:search_rank, :float, virtual: true)
  104. field(:search_type, :integer, virtual: true)
  105. field(:tags, {:array, :string}, default: [])
  106. field(:last_refreshed_at, :naive_datetime_usec)
  107. field(:last_digest_emailed_at, :naive_datetime)
  108. field(:banner, :map, default: %{})
  109. field(:background, :map, default: %{})
  110. field(:note_count, :integer, default: 0)
  111. field(:follower_count, :integer, default: 0)
  112. field(:following_count, :integer, default: 0)
  113. field(:is_locked, :boolean, default: false)
  114. field(:is_confirmed, :boolean, default: true)
  115. field(:password_reset_pending, :boolean, default: false)
  116. field(:is_approved, :boolean, default: true)
  117. field(:registration_reason, :string, default: nil)
  118. field(:confirmation_token, :string, default: nil)
  119. field(:default_scope, :string, default: "public")
  120. field(:domain_blocks, {:array, :string}, default: [])
  121. field(:is_active, :boolean, default: true)
  122. field(:no_rich_text, :boolean, default: false)
  123. field(:is_moderator, :boolean, default: false)
  124. field(:is_admin, :boolean, default: false)
  125. field(:show_role, :boolean, default: true)
  126. field(:uri, ObjectValidators.Uri, default: nil)
  127. field(:hide_followers_count, :boolean, default: false)
  128. field(:hide_follows_count, :boolean, default: false)
  129. field(:hide_followers, :boolean, default: false)
  130. field(:hide_follows, :boolean, default: false)
  131. field(:hide_favorites, :boolean, default: true)
  132. field(:email_notifications, :map, default: %{"digest" => false})
  133. field(:mascot, :map, default: nil)
  134. field(:emoji, :map, default: %{})
  135. field(:pleroma_settings_store, :map, default: %{})
  136. field(:fields, {:array, :map}, default: [])
  137. field(:raw_fields, {:array, :map}, default: [])
  138. field(:is_discoverable, :boolean, default: false)
  139. field(:invisible, :boolean, default: false)
  140. field(:allow_following_move, :boolean, default: true)
  141. field(:skip_thread_containment, :boolean, default: false)
  142. field(:actor_type, :string, default: "Person")
  143. field(:also_known_as, {:array, ObjectValidators.ObjectID}, default: [])
  144. field(:inbox, :string)
  145. field(:shared_inbox, :string)
  146. field(:accepts_chat_messages, :boolean, default: nil)
  147. field(:last_active_at, :naive_datetime)
  148. field(:disclose_client, :boolean, default: true)
  149. field(:pinned_objects, :map, default: %{})
  150. field(:is_suggested, :boolean, default: false)
  151. field(:last_status_at, :naive_datetime)
  152. field(:birthday, :date)
  153. field(:show_birthday, :boolean, default: false)
  154. field(:language, :string)
  155. embeds_one(
  156. :notification_settings,
  157. Pleroma.User.NotificationSetting,
  158. on_replace: :update
  159. )
  160. has_many(:notifications, Notification)
  161. has_many(:registrations, Registration)
  162. has_many(:deliveries, Delivery)
  163. has_many(:outgoing_relationships, UserRelationship, foreign_key: :source_id)
  164. has_many(:incoming_relationships, UserRelationship, foreign_key: :target_id)
  165. many_to_many(:followed_hashtags, Hashtag,
  166. on_replace: :delete,
  167. on_delete: :delete_all,
  168. join_through: HashtagFollow
  169. )
  170. for {relationship_type,
  171. [
  172. {outgoing_relation, outgoing_relation_target},
  173. {incoming_relation, incoming_relation_source}
  174. ]} <- @user_relationships_config do
  175. # Definitions of `has_many` relations: :blocker_blocks, :muter_mutes, :reblog_muter_mutes,
  176. # :notification_muter_mutes, :subscribee_subscriptions, :endorser_endorsements
  177. has_many(outgoing_relation, UserRelationship,
  178. foreign_key: :source_id,
  179. where: [relationship_type: relationship_type]
  180. )
  181. # Definitions of `has_many` relations: :blockee_blocks, :mutee_mutes, :reblog_mutee_mutes,
  182. # :notification_mutee_mutes, :subscriber_subscriptions, :endorsee_endorsements
  183. has_many(incoming_relation, UserRelationship,
  184. foreign_key: :target_id,
  185. where: [relationship_type: relationship_type]
  186. )
  187. # Definitions of `has_many` relations: :blocked_users, :muted_users, :reblog_muted_users,
  188. # :notification_muted_users, :subscriber_users, :endorsed_users
  189. has_many(outgoing_relation_target, through: [outgoing_relation, :target])
  190. # Definitions of `has_many` relations: :blocker_users, :muter_users, :reblog_muter_users,
  191. # :notification_muter_users, :subscribee_users, :endorser_users
  192. has_many(incoming_relation_source, through: [incoming_relation, :source])
  193. end
  194. # `:blocks` is deprecated (replaced with `blocked_users` relation)
  195. field(:blocks, {:array, :string}, default: [])
  196. # `:mutes` is deprecated (replaced with `muted_users` relation)
  197. field(:mutes, {:array, :string}, default: [])
  198. # `:muted_reblogs` is deprecated (replaced with `reblog_muted_users` relation)
  199. field(:muted_reblogs, {:array, :string}, default: [])
  200. # `:muted_notifications` is deprecated (replaced with `notification_muted_users` relation)
  201. field(:muted_notifications, {:array, :string}, default: [])
  202. # `:subscribers` is deprecated (replaced with `subscriber_users` relation)
  203. field(:subscribers, {:array, :string}, default: [])
  204. embeds_one(
  205. :multi_factor_authentication_settings,
  206. MFA.Settings,
  207. on_replace: :delete
  208. )
  209. timestamps()
  210. end
  211. for {_relationship_type, [{_outgoing_relation, outgoing_relation_target}, _]} <-
  212. @user_relationships_config do
  213. # `def blocked_users_relation/2`, `def muted_users_relation/2`,
  214. # `def reblog_muted_users_relation/2`, `def notification_muted_users/2`,
  215. # `def subscriber_users/2`, `def endorsed_users_relation/2`
  216. def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do
  217. target_users_query = assoc(user, unquote(outgoing_relation_target))
  218. if restrict_deactivated? do
  219. target_users_query
  220. |> User.Query.build(%{deactivated: false})
  221. else
  222. target_users_query
  223. end
  224. end
  225. # `def blocked_users/2`, `def muted_users/2`, `def reblog_muted_users/2`,
  226. # `def notification_muted_users/2`, `def subscriber_users/2`, `def endorsed_users/2`
  227. def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do
  228. __MODULE__
  229. |> apply(unquote(:"#{outgoing_relation_target}_relation"), [
  230. user,
  231. restrict_deactivated?
  232. ])
  233. |> Repo.all()
  234. end
  235. # `def blocked_users_ap_ids/2`, `def muted_users_ap_ids/2`, `def reblog_muted_users_ap_ids/2`,
  236. # `def notification_muted_users_ap_ids/2`, `def subscriber_users_ap_ids/2`,
  237. # `def endorsed_users_ap_ids/2`
  238. def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \\ false) do
  239. __MODULE__
  240. |> apply(unquote(:"#{outgoing_relation_target}_relation"), [
  241. user,
  242. restrict_deactivated?
  243. ])
  244. |> select([u], u.ap_id)
  245. |> Repo.all()
  246. end
  247. end
  248. def cached_blocked_users_ap_ids(user) do
  249. @cachex.fetch!(:user_cache, "blocked_users_ap_ids:#{user.ap_id}", fn _ ->
  250. blocked_users_ap_ids(user)
  251. end)
  252. end
  253. def cached_muted_users_ap_ids(user) do
  254. @cachex.fetch!(:user_cache, "muted_users_ap_ids:#{user.ap_id}", fn _ ->
  255. muted_users_ap_ids(user)
  256. end)
  257. end
  258. defdelegate following_count(user), to: FollowingRelationship
  259. defdelegate following(user), to: FollowingRelationship
  260. defdelegate following?(follower, followed), to: FollowingRelationship
  261. defdelegate following_ap_ids(user), to: FollowingRelationship
  262. defdelegate get_follow_requests(user), to: FollowingRelationship
  263. defdelegate search(query, opts \\ []), to: User.Search
  264. @doc """
  265. Dumps Flake Id to SQL-compatible format (16-byte UUID).
  266. E.g. "9pQtDGXuq4p3VlcJEm" -> <<0, 0, 1, 110, 179, 218, 42, 92, 213, 41, 44, 227, 95, 213, 0, 0>>
  267. """
  268. def binary_id(source_id) when is_binary(source_id) do
  269. with {:ok, dumped_id} <- FlakeId.Ecto.CompatType.dump(source_id) do
  270. dumped_id
  271. else
  272. _ -> source_id
  273. end
  274. end
  275. def binary_id(source_ids) when is_list(source_ids) do
  276. Enum.map(source_ids, &binary_id/1)
  277. end
  278. def binary_id(%User{} = user), do: binary_id(user.id)
  279. @doc "Returns status account"
  280. @spec account_status(User.t()) :: account_status()
  281. def account_status(%User{is_active: false}), do: :deactivated
  282. def account_status(%User{password_reset_pending: true}), do: :password_reset_pending
  283. def account_status(%User{local: true, is_approved: false}), do: :approval_pending
  284. def account_status(%User{local: true, is_confirmed: false}), do: :confirmation_pending
  285. def account_status(%User{}), do: :active
  286. @spec visible_for(User.t(), User.t() | nil) ::
  287. :visible
  288. | :invisible
  289. | :restricted_unauthenticated
  290. | :deactivated
  291. | :confirmation_pending
  292. def visible_for(user, for_user \\ nil)
  293. def visible_for(%User{invisible: true}, _), do: :invisible
  294. def visible_for(%User{id: user_id}, %User{id: user_id}), do: :visible
  295. def visible_for(%User{} = user, nil) do
  296. if restrict_unauthenticated?(user) do
  297. :restrict_unauthenticated
  298. else
  299. visible_account_status(user)
  300. end
  301. end
  302. def visible_for(%User{} = user, for_user) do
  303. if privileged?(for_user, :users_manage_activation_state) do
  304. :visible
  305. else
  306. visible_account_status(user)
  307. end
  308. end
  309. def visible_for(_, _), do: :invisible
  310. defp restrict_unauthenticated?(%User{local: true}) do
  311. Config.restrict_unauthenticated_access?(:profiles, :local)
  312. end
  313. defp restrict_unauthenticated?(%User{local: _}) do
  314. Config.restrict_unauthenticated_access?(:profiles, :remote)
  315. end
  316. defp visible_account_status(user) do
  317. status = account_status(user)
  318. if status in [:active, :password_reset_pending] do
  319. :visible
  320. else
  321. status
  322. end
  323. end
  324. @spec privileged?(User.t(), atom()) :: boolean()
  325. def privileged?(%User{is_admin: false, is_moderator: false}, _), do: false
  326. def privileged?(
  327. %User{local: true, is_admin: is_admin, is_moderator: is_moderator},
  328. privilege_tag
  329. ),
  330. do:
  331. privileged_for?(privilege_tag, is_admin, :admin_privileges) or
  332. privileged_for?(privilege_tag, is_moderator, :moderator_privileges)
  333. def privileged?(_, _), do: false
  334. defp privileged_for?(privilege_tag, true, config_role_key),
  335. do: privilege_tag in Config.get([:instance, config_role_key])
  336. defp privileged_for?(_, _, _), do: false
  337. @spec privileges(User.t()) :: [atom()]
  338. def privileges(%User{local: false}) do
  339. []
  340. end
  341. def privileges(%User{is_moderator: false, is_admin: false}) do
  342. []
  343. end
  344. def privileges(%User{local: true, is_moderator: true, is_admin: true}) do
  345. (Config.get([:instance, :moderator_privileges]) ++ Config.get([:instance, :admin_privileges]))
  346. |> Enum.uniq()
  347. end
  348. def privileges(%User{local: true, is_moderator: true, is_admin: false}) do
  349. Config.get([:instance, :moderator_privileges])
  350. end
  351. def privileges(%User{local: true, is_moderator: false, is_admin: true}) do
  352. Config.get([:instance, :admin_privileges])
  353. end
  354. @spec invisible?(User.t()) :: boolean()
  355. def invisible?(%User{invisible: true}), do: true
  356. def invisible?(_), do: false
  357. def avatar_url(user, options \\ []) do
  358. case user.avatar do
  359. %{"url" => [%{"href" => href} | _]} ->
  360. href
  361. _ ->
  362. unless options[:no_default] do
  363. Config.get([:assets, :default_user_avatar], "#{Endpoint.url()}/images/avi.png")
  364. end
  365. end
  366. end
  367. def banner_url(user, options \\ []) do
  368. case user.banner do
  369. %{"url" => [%{"href" => href} | _]} -> href
  370. _ -> !options[:no_default] && "#{Endpoint.url()}/images/banner.png"
  371. end
  372. end
  373. def image_description(image, default \\ "")
  374. def image_description(%{"name" => name}, _default), do: name
  375. def image_description(_, default), do: default
  376. # Should probably be renamed or removed
  377. @spec ap_id(User.t()) :: String.t()
  378. def ap_id(%User{nickname: nickname}), do: "#{Endpoint.url()}/users/#{nickname}"
  379. @spec ap_followers(User.t()) :: String.t()
  380. def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
  381. def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers"
  382. @spec ap_following(User.t()) :: String.t()
  383. def ap_following(%User{following_address: fa}) when is_binary(fa), do: fa
  384. def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
  385. @spec ap_featured_collection(User.t()) :: String.t()
  386. def ap_featured_collection(%User{featured_address: fa}) when is_binary(fa), do: fa
  387. def ap_featured_collection(%User{} = user), do: "#{ap_id(user)}/collections/featured"
  388. defp truncate_fields_param(params) do
  389. if Map.has_key?(params, :fields) do
  390. Map.put(params, :fields, Enum.map(params[:fields], &truncate_field/1))
  391. else
  392. params
  393. end
  394. end
  395. defp truncate_if_exists(params, key, max_length) do
  396. if Map.has_key?(params, key) and is_binary(params[key]) do
  397. {value, _chopped} = String.split_at(params[key], max_length)
  398. Map.put(params, key, value)
  399. else
  400. params
  401. end
  402. end
  403. defp fix_follower_address(%{follower_address: _, following_address: _} = params), do: params
  404. defp fix_follower_address(%{nickname: nickname} = params),
  405. do: Map.put(params, :follower_address, ap_followers(%User{nickname: nickname}))
  406. defp fix_follower_address(params), do: params
  407. def remote_user_changeset(struct \\ %User{local: false}, params) do
  408. bio_limit = Config.get([:instance, :user_bio_length], 5000)
  409. name_limit = Config.get([:instance, :user_name_length], 100)
  410. fields_limit = Config.get([:instance, :max_remote_account_fields], 0)
  411. name =
  412. case params[:name] do
  413. name when is_binary(name) and byte_size(name) > 0 -> name
  414. _ -> params[:nickname]
  415. end
  416. params =
  417. params
  418. |> Map.put(:name, name)
  419. |> Map.put_new(:last_refreshed_at, NaiveDateTime.utc_now())
  420. |> truncate_if_exists(:name, name_limit)
  421. |> truncate_if_exists(:bio, bio_limit)
  422. |> Map.update(:fields, [], &Enum.take(&1, fields_limit))
  423. |> truncate_fields_param()
  424. |> fix_follower_address()
  425. struct
  426. |> cast(
  427. params,
  428. [
  429. :bio,
  430. :emoji,
  431. :ap_id,
  432. :inbox,
  433. :shared_inbox,
  434. :nickname,
  435. :public_key,
  436. :avatar,
  437. :banner,
  438. :is_locked,
  439. :last_refreshed_at,
  440. :uri,
  441. :follower_address,
  442. :following_address,
  443. :featured_address,
  444. :hide_followers,
  445. :hide_follows,
  446. :hide_followers_count,
  447. :hide_follows_count,
  448. :follower_count,
  449. :fields,
  450. :following_count,
  451. :is_discoverable,
  452. :invisible,
  453. :actor_type,
  454. :also_known_as,
  455. :accepts_chat_messages,
  456. :pinned_objects,
  457. :birthday,
  458. :show_birthday
  459. ]
  460. )
  461. |> cast(params, [:name], empty_values: [])
  462. |> validate_required([:ap_id])
  463. |> validate_required([:name], trim: false)
  464. |> unique_constraint(:nickname)
  465. |> validate_format(:nickname, @email_regex)
  466. |> validate_length(:bio, max: bio_limit)
  467. |> validate_length(:name, max: name_limit)
  468. |> validate_fields(true)
  469. |> validate_non_local()
  470. end
  471. defp validate_non_local(cng) do
  472. local? = get_field(cng, :local)
  473. if local? do
  474. cng
  475. |> add_error(:local, "User is local, can't update with this changeset.")
  476. else
  477. cng
  478. end
  479. end
  480. def update_changeset(struct, params \\ %{}) do
  481. bio_limit = Config.get([:instance, :user_bio_length], 5000)
  482. name_limit = Config.get([:instance, :user_name_length], 100)
  483. struct
  484. |> cast(
  485. params,
  486. [
  487. :bio,
  488. :raw_bio,
  489. :name,
  490. :emoji,
  491. :avatar,
  492. :public_key,
  493. :inbox,
  494. :shared_inbox,
  495. :is_locked,
  496. :no_rich_text,
  497. :default_scope,
  498. :banner,
  499. :hide_follows,
  500. :hide_followers,
  501. :hide_followers_count,
  502. :hide_follows_count,
  503. :hide_favorites,
  504. :allow_following_move,
  505. :also_known_as,
  506. :background,
  507. :show_role,
  508. :skip_thread_containment,
  509. :fields,
  510. :raw_fields,
  511. :pleroma_settings_store,
  512. :is_discoverable,
  513. :actor_type,
  514. :accepts_chat_messages,
  515. :disclose_client,
  516. :birthday,
  517. :show_birthday
  518. ]
  519. )
  520. |> validate_min_age()
  521. |> unique_constraint(:nickname)
  522. |> validate_format(:nickname, local_nickname_regex())
  523. |> validate_length(:bio, max: bio_limit)
  524. |> validate_length(:name, min: 1, max: name_limit)
  525. |> validate_inclusion(:actor_type, Pleroma.Constants.allowed_user_actor_types())
  526. |> validate_image_description(:avatar_description, params)
  527. |> validate_image_description(:header_description, params)
  528. |> put_fields()
  529. |> put_emoji()
  530. |> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
  531. |> put_change_if_present(
  532. :avatar,
  533. &put_upload(&1, :avatar, Map.get(params, :avatar_description))
  534. )
  535. |> put_change_if_present(
  536. :banner,
  537. &put_upload(&1, :banner, Map.get(params, :header_description))
  538. )
  539. |> put_change_if_present(:background, &put_upload(&1, :background))
  540. |> put_change_if_present(
  541. :pleroma_settings_store,
  542. &{:ok, Map.merge(struct.pleroma_settings_store, &1)}
  543. )
  544. |> maybe_update_image_description(:avatar, Map.get(params, :avatar_description))
  545. |> maybe_update_image_description(:banner, Map.get(params, :header_description))
  546. |> validate_fields(false)
  547. end
  548. defp put_fields(changeset) do
  549. if raw_fields = get_change(changeset, :raw_fields) do
  550. old_fields = changeset.data.raw_fields
  551. raw_fields =
  552. raw_fields
  553. |> Enum.filter(fn %{"name" => n} -> n != "" end)
  554. |> Enum.map(fn field ->
  555. previous =
  556. old_fields
  557. |> Enum.find(fn %{"value" => value} -> field["value"] == value end)
  558. if previous && Map.has_key?(previous, "verified_at") do
  559. field
  560. |> Map.put("verified_at", previous["verified_at"])
  561. else
  562. field
  563. end
  564. end)
  565. fields =
  566. raw_fields
  567. |> Enum.map(fn f -> Map.update!(f, "value", &parse_fields(&1)) end)
  568. changeset
  569. |> put_change(:raw_fields, raw_fields)
  570. |> put_change(:fields, fields)
  571. else
  572. changeset
  573. end
  574. end
  575. defp parse_fields(value) do
  576. value
  577. |> Formatter.linkify(mentions_format: :full)
  578. |> elem(0)
  579. end
  580. defp put_emoji(changeset) do
  581. emojified_fields = [:bio, :name, :raw_fields]
  582. if Enum.any?(changeset.changes, fn {k, _} -> k in emojified_fields end) do
  583. bio = Emoji.Formatter.get_emoji_map(get_field(changeset, :bio))
  584. name = Emoji.Formatter.get_emoji_map(get_field(changeset, :name))
  585. emoji = Map.merge(bio, name)
  586. emoji =
  587. changeset
  588. |> get_field(:raw_fields)
  589. |> Enum.reduce(emoji, fn x, acc ->
  590. Map.merge(acc, Emoji.Formatter.get_emoji_map(x["name"] <> x["value"]))
  591. end)
  592. put_change(changeset, :emoji, emoji)
  593. else
  594. changeset
  595. end
  596. end
  597. defp put_change_if_present(changeset, map_field, value_function) do
  598. with {:ok, value} <- fetch_change(changeset, map_field),
  599. {:ok, new_value} <- value_function.(value) do
  600. put_change(changeset, map_field, new_value)
  601. else
  602. {:error, :file_too_large} ->
  603. Ecto.Changeset.validate_change(changeset, map_field, fn map_field, _value ->
  604. [{map_field, "file is too large"}]
  605. end)
  606. _ ->
  607. changeset
  608. end
  609. end
  610. defp put_upload(value, type, description \\ nil) do
  611. with %Plug.Upload{} <- value,
  612. {:ok, object} <- ActivityPub.upload(value, type: type, description: description) do
  613. {:ok, object.data}
  614. end
  615. end
  616. defp validate_image_description(changeset, key, params) do
  617. description_limit = Config.get([:instance, :description_limit], 5_000)
  618. description = Map.get(params, key)
  619. if is_binary(description) and String.length(description) > description_limit do
  620. changeset
  621. |> add_error(key, "#{key} is too long")
  622. else
  623. changeset
  624. end
  625. end
  626. defp maybe_update_image_description(changeset, image_field, description)
  627. when is_binary(description) do
  628. with {:image_missing, true} <- {:image_missing, not changed?(changeset, image_field)},
  629. {:existing_image, %{"id" => id}} <-
  630. {:existing_image, Map.get(changeset.data, image_field)},
  631. {:object, %Object{} = object} <- {:object, Object.get_by_ap_id(id)},
  632. {:ok, object} <- Object.update_data(object, %{"name" => description}) do
  633. put_change(changeset, image_field, object.data)
  634. else
  635. {:description_too_long, true} -> {:error}
  636. _ -> changeset
  637. end
  638. end
  639. defp maybe_update_image_description(changeset, _, _), do: changeset
  640. def update_as_admin_changeset(struct, params) do
  641. struct
  642. |> update_changeset(params)
  643. |> cast(params, [:email])
  644. |> delete_change(:also_known_as)
  645. |> unique_constraint(:email)
  646. |> validate_format(:email, @email_regex)
  647. |> validate_inclusion(:actor_type, ["Person", "Service"])
  648. end
  649. @spec update_as_admin(User.t(), map()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
  650. def update_as_admin(user, params) do
  651. params = Map.put(params, "password_confirmation", params["password"])
  652. changeset = update_as_admin_changeset(user, params)
  653. if params["password"] do
  654. reset_password(user, changeset, params)
  655. else
  656. User.update_and_set_cache(changeset)
  657. end
  658. end
  659. def password_update_changeset(struct, params) do
  660. struct
  661. |> cast(params, [:password, :password_confirmation])
  662. |> validate_required([:password, :password_confirmation])
  663. |> validate_confirmation(:password)
  664. |> put_password_hash()
  665. |> put_change(:password_reset_pending, false)
  666. end
  667. @spec reset_password(User.t(), map()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
  668. def reset_password(%User{} = user, params) do
  669. reset_password(user, user, params)
  670. end
  671. def reset_password(%User{id: user_id} = user, struct, params) do
  672. multi =
  673. Multi.new()
  674. |> Multi.update(:user, password_update_changeset(struct, params))
  675. |> Multi.delete_all(:tokens, OAuth.Token.Query.get_by_user(user_id))
  676. |> Multi.delete_all(:auth, OAuth.Authorization.delete_by_user_query(user))
  677. case Repo.transaction(multi) do
  678. {:ok, %{user: user} = _} -> set_cache(user)
  679. {:error, _, changeset, _} -> {:error, changeset}
  680. end
  681. end
  682. def update_password_reset_pending(user, value) do
  683. user
  684. |> change()
  685. |> put_change(:password_reset_pending, value)
  686. |> update_and_set_cache()
  687. end
  688. def force_password_reset_async(user) do
  689. BackgroundWorker.new(%{"op" => "force_password_reset", "user_id" => user.id})
  690. |> Oban.insert()
  691. end
  692. @spec force_password_reset(User.t()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
  693. def force_password_reset(user), do: update_password_reset_pending(user, true)
  694. # Used to auto-register LDAP accounts which won't have a password hash stored locally
  695. def register_changeset_ldap(struct, params = %{password: password})
  696. when is_nil(password) do
  697. params = Map.put_new(params, :accepts_chat_messages, true)
  698. params =
  699. if Map.has_key?(params, :email) do
  700. Map.put_new(params, :email, params[:email])
  701. else
  702. params
  703. end
  704. struct
  705. |> cast(params, [
  706. :name,
  707. :nickname,
  708. :email,
  709. :accepts_chat_messages
  710. ])
  711. |> validate_required([:name, :nickname])
  712. |> unique_constraint(:nickname)
  713. |> validate_not_restricted_nickname(:nickname)
  714. |> validate_format(:nickname, local_nickname_regex())
  715. |> put_ap_id()
  716. |> unique_constraint(:ap_id)
  717. |> put_following_and_follower_and_featured_address()
  718. |> put_private_key()
  719. end
  720. def register_changeset(struct, params \\ %{}, opts \\ []) do
  721. bio_limit = Config.get([:instance, :user_bio_length], 5000)
  722. name_limit = Config.get([:instance, :user_name_length], 100)
  723. reason_limit = Config.get([:instance, :registration_reason_length], 500)
  724. params = Map.put_new(params, :accepts_chat_messages, true)
  725. confirmed? =
  726. if is_nil(opts[:confirmed]) do
  727. !Config.get([:instance, :account_activation_required])
  728. else
  729. opts[:confirmed]
  730. end
  731. approved? =
  732. if is_nil(opts[:approved]) do
  733. !Config.get([:instance, :account_approval_required])
  734. else
  735. opts[:approved]
  736. end
  737. struct
  738. |> confirmation_changeset(set_confirmation: confirmed?)
  739. |> approval_changeset(set_approval: approved?)
  740. |> cast(params, [
  741. :bio,
  742. :raw_bio,
  743. :email,
  744. :name,
  745. :nickname,
  746. :password,
  747. :password_confirmation,
  748. :emoji,
  749. :accepts_chat_messages,
  750. :registration_reason,
  751. :birthday,
  752. :language
  753. ])
  754. |> validate_required([:name, :nickname, :password, :password_confirmation])
  755. |> validate_confirmation(:password)
  756. |> unique_constraint(:email)
  757. |> validate_format(:email, @email_regex)
  758. |> validate_email_not_in_blacklisted_domain(:email)
  759. |> unique_constraint(:nickname)
  760. |> validate_not_restricted_nickname(:nickname)
  761. |> validate_format(:nickname, local_nickname_regex())
  762. |> validate_length(:bio, max: bio_limit)
  763. |> validate_length(:name, min: 1, max: name_limit)
  764. |> validate_length(:registration_reason, max: reason_limit)
  765. |> maybe_validate_required_email(opts[:external])
  766. |> maybe_validate_required_birthday
  767. |> validate_min_age()
  768. |> put_password_hash
  769. |> put_ap_id()
  770. |> unique_constraint(:ap_id)
  771. |> put_following_and_follower_and_featured_address()
  772. |> put_private_key()
  773. end
  774. def validate_not_restricted_nickname(changeset, field) do
  775. validate_change(changeset, field, fn _, value ->
  776. valid? =
  777. Config.get([User, :restricted_nicknames])
  778. |> Enum.all?(fn restricted_nickname ->
  779. String.downcase(value) != String.downcase(restricted_nickname)
  780. end)
  781. if valid?, do: [], else: [nickname: "Invalid nickname"]
  782. end)
  783. end
  784. def validate_email_not_in_blacklisted_domain(changeset, field) do
  785. validate_change(changeset, field, fn _, value ->
  786. valid? =
  787. Config.get([User, :email_blacklist])
  788. |> Enum.all?(fn blacklisted_domain ->
  789. blacklisted_domain_downcase = String.downcase(blacklisted_domain)
  790. !String.ends_with?(String.downcase(value), [
  791. "@" <> blacklisted_domain_downcase,
  792. "." <> blacklisted_domain_downcase
  793. ])
  794. end)
  795. if valid?, do: [], else: [email: "Invalid email"]
  796. end)
  797. end
  798. def maybe_validate_required_email(changeset, true), do: changeset
  799. def maybe_validate_required_email(changeset, _) do
  800. if Config.get([:instance, :account_activation_required]) do
  801. validate_required(changeset, [:email])
  802. else
  803. changeset
  804. end
  805. end
  806. defp maybe_validate_required_birthday(changeset) do
  807. if Config.get([:instance, :birthday_required]) do
  808. validate_required(changeset, [:birthday])
  809. else
  810. changeset
  811. end
  812. end
  813. defp validate_min_age(changeset) do
  814. changeset
  815. |> validate_change(:birthday, fn :birthday, birthday ->
  816. valid? =
  817. Date.utc_today()
  818. |> Date.diff(birthday) >=
  819. Config.get([:instance, :birthday_min_age])
  820. if valid?, do: [], else: [birthday: "Invalid age"]
  821. end)
  822. end
  823. defp put_ap_id(changeset) do
  824. ap_id = ap_id(%User{nickname: get_field(changeset, :nickname)})
  825. put_change(changeset, :ap_id, ap_id)
  826. end
  827. defp put_following_and_follower_and_featured_address(changeset) do
  828. user = %User{nickname: get_field(changeset, :nickname)}
  829. followers = ap_followers(user)
  830. following = ap_following(user)
  831. featured = ap_featured_collection(user)
  832. changeset
  833. |> put_change(:follower_address, followers)
  834. |> put_change(:following_address, following)
  835. |> put_change(:featured_address, featured)
  836. end
  837. defp put_private_key(changeset) do
  838. {:ok, pem} = Keys.generate_rsa_pem()
  839. put_change(changeset, :keys, pem)
  840. end
  841. defp autofollow_users(user) do
  842. candidates = Config.get([:instance, :autofollowed_nicknames])
  843. autofollowed_users =
  844. User.Query.build(%{nickname: candidates, local: true, is_active: true})
  845. |> Repo.all()
  846. follow_all(user, autofollowed_users)
  847. end
  848. defp autofollowing_users(user) do
  849. candidates = Config.get([:instance, :autofollowing_nicknames])
  850. User.Query.build(%{nickname: candidates, local: true, deactivated: false})
  851. |> Repo.all()
  852. |> Enum.each(&follow(&1, user, :follow_accept))
  853. {:ok, :success}
  854. end
  855. @doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
  856. def register(%Ecto.Changeset{} = changeset) do
  857. with {:ok, user} <- Repo.insert(changeset) do
  858. post_register_action(user)
  859. end
  860. end
  861. def post_register_action(%User{is_confirmed: false} = user) do
  862. with {:ok, _} <- maybe_send_confirmation_email(user) do
  863. {:ok, user}
  864. end
  865. end
  866. def post_register_action(%User{is_approved: false} = user) do
  867. with {:ok, _} <- send_user_approval_email(user),
  868. {:ok, _} <- send_admin_approval_emails(user) do
  869. {:ok, user}
  870. end
  871. end
  872. def post_register_action(%User{is_approved: true, is_confirmed: true} = user) do
  873. with {:ok, user} <- autofollow_users(user),
  874. {:ok, _} <- autofollowing_users(user),
  875. {:ok, user} <- set_cache(user),
  876. {:ok, _} <- maybe_send_registration_email(user),
  877. {:ok, _} <- maybe_send_welcome_email(user),
  878. {:ok, _} <- maybe_send_welcome_message(user),
  879. {:ok, _} <- maybe_send_welcome_chat_message(user) do
  880. {:ok, user}
  881. end
  882. end
  883. defp send_user_approval_email(%User{email: email} = user) when is_binary(email) do
  884. user
  885. |> Pleroma.Emails.UserEmail.approval_pending_email()
  886. |> Pleroma.Emails.Mailer.deliver_async()
  887. {:ok, :enqueued}
  888. end
  889. defp send_user_approval_email(_user) do
  890. {:ok, :skipped}
  891. end
  892. defp send_admin_approval_emails(user) do
  893. all_superusers()
  894. |> Enum.filter(fn user -> not is_nil(user.email) end)
  895. |> Enum.each(fn superuser ->
  896. superuser
  897. |> Pleroma.Emails.AdminEmail.new_unapproved_registration(user)
  898. |> Pleroma.Emails.Mailer.deliver_async()
  899. end)
  900. {:ok, :enqueued}
  901. end
  902. defp maybe_send_welcome_message(user) do
  903. if User.WelcomeMessage.enabled?() do
  904. User.WelcomeMessage.post_message(user)
  905. {:ok, :enqueued}
  906. else
  907. {:ok, :noop}
  908. end
  909. end
  910. defp maybe_send_welcome_chat_message(user) do
  911. if User.WelcomeChatMessage.enabled?() do
  912. User.WelcomeChatMessage.post_message(user)
  913. {:ok, :enqueued}
  914. else
  915. {:ok, :noop}
  916. end
  917. end
  918. defp maybe_send_welcome_email(%User{email: email} = user) when is_binary(email) do
  919. if User.WelcomeEmail.enabled?() do
  920. User.WelcomeEmail.send_email(user)
  921. {:ok, :enqueued}
  922. else
  923. {:ok, :noop}
  924. end
  925. end
  926. defp maybe_send_welcome_email(_), do: {:ok, :noop}
  927. @spec maybe_send_confirmation_email(User.t()) :: {:ok, :enqueued | :noop}
  928. def maybe_send_confirmation_email(%User{is_confirmed: false, email: email} = user)
  929. when is_binary(email) do
  930. if Config.get([:instance, :account_activation_required]) do
  931. send_confirmation_email(user)
  932. {:ok, :enqueued}
  933. else
  934. {:ok, :noop}
  935. end
  936. end
  937. def maybe_send_confirmation_email(_), do: {:ok, :noop}
  938. @spec send_confirmation_email(User.t()) :: User.t()
  939. def send_confirmation_email(%User{} = user) do
  940. user
  941. |> Pleroma.Emails.UserEmail.account_confirmation_email()
  942. |> Pleroma.Emails.Mailer.deliver_async()
  943. user
  944. end
  945. @spec maybe_send_registration_email(User.t()) :: {:ok, :enqueued | :noop}
  946. defp maybe_send_registration_email(%User{email: email} = user) when is_binary(email) do
  947. with false <- User.WelcomeEmail.enabled?(),
  948. false <- Config.get([:instance, :account_activation_required], false),
  949. false <- Config.get([:instance, :account_approval_required], false) do
  950. user
  951. |> Pleroma.Emails.UserEmail.successful_registration_email()
  952. |> Pleroma.Emails.Mailer.deliver_async()
  953. {:ok, :enqueued}
  954. else
  955. _ ->
  956. {:ok, :noop}
  957. end
  958. end
  959. defp maybe_send_registration_email(_), do: {:ok, :noop}
  960. def needs_update?(%User{local: true}), do: false
  961. def needs_update?(%User{local: false, last_refreshed_at: nil}), do: true
  962. def needs_update?(%User{local: false} = user) do
  963. NaiveDateTime.diff(NaiveDateTime.utc_now(), user.last_refreshed_at) >= 86_400
  964. end
  965. def needs_update?(_), do: true
  966. @spec maybe_direct_follow(User.t(), User.t()) ::
  967. {:ok, User.t(), User.t()} | {:error, String.t()}
  968. # "Locked" (self-locked) users demand explicit authorization of follow requests
  969. def maybe_direct_follow(%User{} = follower, %User{local: true, is_locked: true} = followed) do
  970. follow(follower, followed, :follow_pending)
  971. end
  972. def maybe_direct_follow(%User{} = follower, %User{local: true} = followed) do
  973. follow(follower, followed)
  974. end
  975. def maybe_direct_follow(%User{} = follower, %User{} = followed) do
  976. {:ok, follower, followed}
  977. end
  978. @doc "A mass follow for local users. Respects blocks in both directions but does not create activities."
  979. @spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()}
  980. def follow_all(follower, followeds) do
  981. followeds
  982. |> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end)
  983. |> Enum.each(&follow(follower, &1, :follow_accept))
  984. set_cache(follower)
  985. end
  986. def follow(%User{} = follower, %User{} = followed, state \\ :follow_accept) do
  987. deny_follow_blocked = Config.get([:user, :deny_follow_blocked])
  988. cond do
  989. not followed.is_active ->
  990. {:error, "Could not follow user: #{followed.nickname} is deactivated."}
  991. deny_follow_blocked and blocks?(followed, follower) ->
  992. {:error, "Could not follow user: #{followed.nickname} blocked you."}
  993. true ->
  994. FollowingRelationship.follow(follower, followed, state)
  995. end
  996. end
  997. def unfollow(%User{ap_id: ap_id}, %User{ap_id: ap_id}) do
  998. {:error, "Not subscribed!"}
  999. end
  1000. @spec unfollow(User.t(), User.t()) :: {:ok, User.t(), Activity.t()} | {:error, String.t()}
  1001. def unfollow(%User{} = follower, %User{} = followed) do
  1002. case do_unfollow(follower, followed) do
  1003. {:ok, follower, followed} ->
  1004. {:ok, follower, Utils.fetch_latest_follow(follower, followed)}
  1005. error ->
  1006. error
  1007. end
  1008. end
  1009. @spec do_unfollow(User.t(), User.t()) :: {:ok, User.t(), User.t()} | {:error, String.t()}
  1010. defp do_unfollow(%User{} = follower, %User{} = followed) do
  1011. case get_follow_state(follower, followed) do
  1012. state when state in [:follow_pending, :follow_accept] ->
  1013. FollowingRelationship.unfollow(follower, followed)
  1014. nil ->
  1015. {:error, "Not subscribed!"}
  1016. end
  1017. end
  1018. @doc "Returns follow state as Pleroma.FollowingRelationship.State value"
  1019. def get_follow_state(%User{} = follower, %User{} = following) do
  1020. following_relationship = FollowingRelationship.get(follower, following)
  1021. get_follow_state(follower, following, following_relationship)
  1022. end
  1023. def get_follow_state(
  1024. %User{} = follower,
  1025. %User{} = following,
  1026. following_relationship
  1027. ) do
  1028. case {following_relationship, following.local} do
  1029. {nil, false} ->
  1030. case Utils.fetch_latest_follow(follower, following) do
  1031. %Activity{data: %{"state" => state}} when state in ["pending", "accept"] ->
  1032. FollowingRelationship.state_to_enum(state)
  1033. _ ->
  1034. nil
  1035. end
  1036. {%{state: state}, _} ->
  1037. state
  1038. {nil, _} ->
  1039. nil
  1040. end
  1041. end
  1042. def locked?(%User{} = user) do
  1043. user.is_locked || false
  1044. end
  1045. def get_by_id(id) do
  1046. Repo.get_by(User, id: id)
  1047. end
  1048. def get_by_ap_id(ap_id) do
  1049. Repo.get_by(User, ap_id: ap_id)
  1050. end
  1051. def get_by_uri(uri) do
  1052. Repo.get_by(User, uri: uri)
  1053. end
  1054. def get_all_by_ap_id(ap_ids) do
  1055. from(u in __MODULE__,
  1056. where: u.ap_id in ^ap_ids
  1057. )
  1058. |> Repo.all()
  1059. end
  1060. def get_all_by_ids(ids) do
  1061. from(u in __MODULE__, where: u.id in ^ids)
  1062. |> Repo.all()
  1063. end
  1064. # This is mostly an SPC migration fix. This guesses the user nickname by taking the last part
  1065. # of the ap_id and the domain and tries to get that user
  1066. def get_by_guessed_nickname(ap_id) do
  1067. domain = URI.parse(ap_id).host
  1068. name = List.last(String.split(ap_id, "/"))
  1069. nickname = "#{name}@#{domain}"
  1070. get_cached_by_nickname(nickname)
  1071. end
  1072. def set_cache({:ok, user}), do: set_cache(user)
  1073. def set_cache({:error, err}), do: {:error, err}
  1074. def set_cache(%User{} = user) do
  1075. @cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
  1076. @cachex.put(:user_cache, "nickname:#{user.nickname}", user)
  1077. @cachex.put(:user_cache, "friends_ap_ids:#{user.nickname}", get_user_friends_ap_ids(user))
  1078. {:ok, user}
  1079. end
  1080. def update_and_set_cache(struct, params) do
  1081. struct
  1082. |> update_changeset(params)
  1083. |> update_and_set_cache()
  1084. end
  1085. def update_and_set_cache(changeset) do
  1086. with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do
  1087. if get_change(changeset, :raw_fields) do
  1088. BackgroundWorker.new(%{"op" => "verify_fields_links", "user_id" => user.id})
  1089. |> Oban.insert()
  1090. end
  1091. set_cache(user)
  1092. end
  1093. end
  1094. def get_user_friends_ap_ids(user) do
  1095. from(u in User.get_friends_query(user), select: u.ap_id)
  1096. |> Repo.all()
  1097. end
  1098. @spec get_cached_user_friends_ap_ids(User.t()) :: [String.t()]
  1099. def get_cached_user_friends_ap_ids(user) do
  1100. @cachex.fetch!(:user_cache, "friends_ap_ids:#{user.ap_id}", fn _ ->
  1101. get_user_friends_ap_ids(user)
  1102. end)
  1103. end
  1104. def invalidate_cache(user) do
  1105. @cachex.del(:user_cache, "ap_id:#{user.ap_id}")
  1106. @cachex.del(:user_cache, "nickname:#{user.nickname}")
  1107. @cachex.del(:user_cache, "friends_ap_ids:#{user.ap_id}")
  1108. @cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
  1109. @cachex.del(:user_cache, "muted_users_ap_ids:#{user.ap_id}")
  1110. end
  1111. @spec get_cached_by_ap_id(String.t()) :: User.t() | nil
  1112. def get_cached_by_ap_id(ap_id) do
  1113. key = "ap_id:#{ap_id}"
  1114. with {:ok, nil} <- @cachex.get(:user_cache, key),
  1115. user when not is_nil(user) <- get_by_ap_id(ap_id),
  1116. {:ok, true} <- @cachex.put(:user_cache, key, user) do
  1117. user
  1118. else
  1119. {:ok, user} -> user
  1120. nil -> nil
  1121. end
  1122. end
  1123. def get_cached_by_id(id) do
  1124. key = "id:#{id}"
  1125. ap_id =
  1126. @cachex.fetch!(:user_cache, key, fn _ ->
  1127. user = get_by_id(id)
  1128. if user do
  1129. @cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
  1130. {:commit, user.ap_id}
  1131. else
  1132. {:ignore, ""}
  1133. end
  1134. end)
  1135. get_cached_by_ap_id(ap_id)
  1136. end
  1137. def get_cached_by_nickname(nickname) do
  1138. key = "nickname:#{nickname}"
  1139. @cachex.fetch!(:user_cache, key, fn _ ->
  1140. case get_or_fetch_by_nickname(nickname) do
  1141. {:ok, user} -> {:commit, user}
  1142. {:error, _error} -> {:ignore, nil}
  1143. end
  1144. end)
  1145. end
  1146. def get_cached_by_nickname_or_id(nickname_or_id, opts \\ []) do
  1147. restrict_to_local = Config.get([:instance, :limit_to_local_content])
  1148. cond do
  1149. is_integer(nickname_or_id) or FlakeId.flake_id?(nickname_or_id) ->
  1150. get_cached_by_id(nickname_or_id) || get_cached_by_nickname(nickname_or_id)
  1151. restrict_to_local == false or not String.contains?(nickname_or_id, "@") ->
  1152. get_cached_by_nickname(nickname_or_id)
  1153. restrict_to_local == :unauthenticated and match?(%User{}, opts[:for]) ->
  1154. get_cached_by_nickname(nickname_or_id)
  1155. true ->
  1156. nil
  1157. end
  1158. end
  1159. @spec get_by_nickname(String.t()) :: User.t() | nil
  1160. def get_by_nickname(nickname) do
  1161. Repo.get_by(User, nickname: nickname) ||
  1162. if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()})i, nickname) do
  1163. Repo.get_by(User, nickname: local_nickname(nickname))
  1164. end
  1165. end
  1166. def get_by_email(email), do: Repo.get_by(User, email: email)
  1167. def get_by_nickname_or_email(nickname_or_email) do
  1168. get_by_nickname(nickname_or_email) || get_by_email(nickname_or_email)
  1169. end
  1170. def fetch_by_nickname(nickname), do: ActivityPub.make_user_from_nickname(nickname)
  1171. def get_or_fetch_by_nickname(nickname) do
  1172. with %User{} = user <- get_by_nickname(nickname) do
  1173. {:ok, user}
  1174. else
  1175. _e ->
  1176. with [_nick, _domain] <- String.split(nickname, "@"),
  1177. {:ok, user} <- fetch_by_nickname(nickname) do
  1178. {:ok, user}
  1179. else
  1180. _e -> {:error, "not found " <> nickname}
  1181. end
  1182. end
  1183. end
  1184. @spec get_followers_query(User.t(), pos_integer() | nil) :: Ecto.Query.t()
  1185. def get_followers_query(%User{} = user, nil) do
  1186. User.Query.build(%{followers: user, is_active: true})
  1187. end
  1188. def get_followers_query(%User{} = user, page) do
  1189. user
  1190. |> get_followers_query(nil)
  1191. |> User.Query.paginate(page, 20)
  1192. end
  1193. @spec get_followers_query(User.t()) :: Ecto.Query.t()
  1194. def get_followers_query(%User{} = user), do: get_followers_query(user, nil)
  1195. @spec get_followers(User.t(), pos_integer() | nil) :: {:ok, list(User.t())}
  1196. def get_followers(%User{} = user, page \\ nil) do
  1197. user
  1198. |> get_followers_query(page)
  1199. |> Repo.all()
  1200. end
  1201. @spec get_external_followers(User.t(), pos_integer() | nil) :: {:ok, list(User.t())}
  1202. def get_external_followers(%User{} = user, page \\ nil) do
  1203. user
  1204. |> get_followers_query(page)
  1205. |> User.Query.build(%{external: true})
  1206. |> Repo.all()
  1207. end
  1208. def get_followers_ids(%User{} = user, page \\ nil) do
  1209. user
  1210. |> get_followers_query(page)
  1211. |> select([u], u.id)
  1212. |> Repo.all()
  1213. end
  1214. @spec get_friends_query(User.t(), pos_integer() | nil) :: Ecto.Query.t()
  1215. def get_friends_query(%User{} = user, nil) do
  1216. User.Query.build(%{friends: user, deactivated: false})
  1217. end
  1218. def get_friends_query(%User{} = user, page) do
  1219. user
  1220. |> get_friends_query(nil)
  1221. |> User.Query.paginate(page, 20)
  1222. end
  1223. @spec get_friends_query(User.t()) :: Ecto.Query.t()
  1224. def get_friends_query(%User{} = user), do: get_friends_query(user, nil)
  1225. def get_friends(%User{} = user, page \\ nil) do
  1226. user
  1227. |> get_friends_query(page)
  1228. |> Repo.all()
  1229. end
  1230. def get_friends_ap_ids(%User{} = user) do
  1231. user
  1232. |> get_friends_query(nil)
  1233. |> select([u], u.ap_id)
  1234. |> Repo.all()
  1235. end
  1236. def get_friends_ids(%User{} = user, page \\ nil) do
  1237. user
  1238. |> get_friends_query(page)
  1239. |> select([u], u.id)
  1240. |> Repo.all()
  1241. end
  1242. @spec get_familiar_followers_query(User.t(), User.t(), pos_integer() | nil) :: Ecto.Query.t()
  1243. def get_familiar_followers_query(%User{} = user, %User{} = current_user, nil) do
  1244. friends =
  1245. get_friends_query(current_user)
  1246. |> where([u], not u.hide_follows)
  1247. |> select([u], u.id)
  1248. User.Query.build(%{is_active: true})
  1249. |> where([u], u.id not in ^[user.id, current_user.id])
  1250. |> join(:inner, [u], r in FollowingRelationship,
  1251. as: :followers_relationships,
  1252. on: r.following_id == ^user.id and r.follower_id == u.id
  1253. )
  1254. |> where([followers_relationships: r], r.state == ^:follow_accept)
  1255. |> where([followers_relationships: r], r.follower_id in subquery(friends))
  1256. end
  1257. def get_familiar_followers_query(%User{} = user, %User{} = current_user, page) do
  1258. user
  1259. |> get_familiar_followers_query(current_user, nil)
  1260. |> User.Query.paginate(page, 20)
  1261. end
  1262. @spec get_familiar_followers_query(User.t(), User.t()) :: Ecto.Query.t()
  1263. def get_familiar_followers_query(%User{} = user, %User{} = current_user),
  1264. do: get_familiar_followers_query(user, current_user, nil)
  1265. @spec get_familiar_followers(User.t(), User.t(), pos_integer() | nil) :: {:ok, list(User.t())}
  1266. def get_familiar_followers(%User{} = user, %User{} = current_user, page \\ nil) do
  1267. user
  1268. |> get_familiar_followers_query(current_user, page)
  1269. |> Repo.all()
  1270. end
  1271. def increase_note_count(%User{} = user) do
  1272. User
  1273. |> where(id: ^user.id)
  1274. |> update([u], inc: [note_count: 1])
  1275. |> select([u], u)
  1276. |> Repo.update_all([])
  1277. |> case do
  1278. {1, [user]} -> set_cache(user)
  1279. _ -> {:error, user}
  1280. end
  1281. end
  1282. def decrease_note_count(%User{} = user) do
  1283. User
  1284. |> where(id: ^user.id)
  1285. |> update([u],
  1286. set: [
  1287. note_count: fragment("greatest(0, note_count - 1)")
  1288. ]
  1289. )
  1290. |> select([u], u)
  1291. |> Repo.update_all([])
  1292. |> case do
  1293. {1, [user]} -> set_cache(user)
  1294. _ -> {:error, user}
  1295. end
  1296. end
  1297. def update_note_count(%User{} = user, note_count \\ nil) do
  1298. note_count =
  1299. note_count ||
  1300. from(
  1301. a in Object,
  1302. where: fragment("?->>'actor' = ? and ?->>'type' = 'Note'", a.data, ^user.ap_id, a.data),
  1303. select: count(a.id)
  1304. )
  1305. |> Repo.one()
  1306. user
  1307. |> cast(%{note_count: note_count}, [:note_count])
  1308. |> update_and_set_cache()
  1309. end
  1310. @spec maybe_fetch_follow_information(User.t()) :: User.t()
  1311. def maybe_fetch_follow_information(user) do
  1312. with {:ok, user} <- fetch_follow_information(user) do
  1313. user
  1314. else
  1315. e ->
  1316. Logger.error("Follower/Following counter update for #{user.ap_id} failed.\n#{inspect(e)}")
  1317. user
  1318. end
  1319. end
  1320. def fetch_follow_information(user) do
  1321. with {:ok, info} <- ActivityPub.fetch_follow_information_for_user(user) do
  1322. user
  1323. |> follow_information_changeset(info)
  1324. |> update_and_set_cache()
  1325. end
  1326. end
  1327. defp follow_information_changeset(user, params) do
  1328. user
  1329. |> cast(params, [
  1330. :hide_followers,
  1331. :hide_follows,
  1332. :follower_count,
  1333. :following_count,
  1334. :hide_followers_count,
  1335. :hide_follows_count
  1336. ])
  1337. end
  1338. @spec update_follower_count(User.t()) :: {:ok, User.t()}
  1339. def update_follower_count(%User{} = user) do
  1340. if user.local or !Config.get([:instance, :external_user_synchronization]) do
  1341. follower_count = FollowingRelationship.follower_count(user)
  1342. user
  1343. |> follow_information_changeset(%{follower_count: follower_count})
  1344. |> update_and_set_cache
  1345. else
  1346. {:ok, maybe_fetch_follow_information(user)}
  1347. end
  1348. end
  1349. @spec update_following_count(User.t()) :: {:ok, User.t()}
  1350. def update_following_count(%User{local: false} = user) do
  1351. if Config.get([:instance, :external_user_synchronization]) do
  1352. {:ok, maybe_fetch_follow_information(user)}
  1353. else
  1354. {:ok, user}
  1355. end
  1356. end
  1357. def update_following_count(%User{local: true} = user) do
  1358. following_count = FollowingRelationship.following_count(user)
  1359. user
  1360. |> follow_information_changeset(%{following_count: following_count})
  1361. |> update_and_set_cache()
  1362. end
  1363. @spec get_users_from_set([String.t()], keyword()) :: [User.t()]
  1364. def get_users_from_set(ap_ids, opts \\ []) do
  1365. local_only = Keyword.get(opts, :local_only, true)
  1366. criteria = %{ap_id: ap_ids, is_active: true}
  1367. criteria = if local_only, do: Map.put(criteria, :local, true), else: criteria
  1368. User.Query.build(criteria)
  1369. |> Repo.all()
  1370. end
  1371. @spec get_recipients_from_activity(Activity.t()) :: [User.t()]
  1372. def get_recipients_from_activity(%Activity{recipients: to, actor: actor}) do
  1373. to = [actor | to]
  1374. query = User.Query.build(%{recipients_from_activity: to, local: true, is_active: true})
  1375. query
  1376. |> Repo.all()
  1377. end
  1378. @spec mute(User.t(), User.t(), map()) ::
  1379. {:ok, list(UserRelationship.t())} | {:error, String.t()}
  1380. def mute(%User{} = muter, %User{} = mutee, params \\ %{}) do
  1381. notifications? = Map.get(params, :notifications, true)
  1382. duration = Map.get(params, :duration, 0)
  1383. expires_at =
  1384. if duration > 0 do
  1385. DateTime.utc_now()
  1386. |> DateTime.add(duration)
  1387. else
  1388. nil
  1389. end
  1390. with {:ok, user_mute} <- UserRelationship.create_mute(muter, mutee, expires_at),
  1391. {:ok, user_notification_mute} <-
  1392. (notifications? &&
  1393. UserRelationship.create_notification_mute(
  1394. muter,
  1395. mutee,
  1396. expires_at
  1397. )) ||
  1398. {:ok, nil} do
  1399. if duration > 0 do
  1400. Pleroma.Workers.MuteExpireWorker.new(
  1401. %{"op" => "unmute_user", "muter_id" => muter.id, "mutee_id" => mutee.id},
  1402. scheduled_at: expires_at
  1403. )
  1404. |> Oban.insert()
  1405. end
  1406. @cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}")
  1407. {:ok, Enum.filter([user_mute, user_notification_mute], & &1)}
  1408. end
  1409. end
  1410. def unmute(%User{} = muter, %User{} = mutee) do
  1411. with {:ok, user_mute} <- UserRelationship.delete_mute(muter, mutee),
  1412. {:ok, user_notification_mute} <-
  1413. UserRelationship.delete_notification_mute(muter, mutee) do
  1414. @cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}")
  1415. {:ok, [user_mute, user_notification_mute]}
  1416. end
  1417. end
  1418. def unmute(muter_id, mutee_id) do
  1419. with {:muter, %User{} = muter} <- {:muter, User.get_by_id(muter_id)},
  1420. {:mutee, %User{} = mutee} <- {:mutee, User.get_by_id(mutee_id)} do
  1421. unmute(muter, mutee)
  1422. else
  1423. {who, result} = error ->
  1424. Logger.warning(
  1425. "User.unmute/2 failed. #{who}: #{result}, muter_id: #{muter_id}, mutee_id: #{mutee_id}"
  1426. )
  1427. {:error, error}
  1428. end
  1429. end
  1430. def subscribe(%User{} = subscriber, %User{} = target) do
  1431. deny_follow_blocked = Config.get([:user, :deny_follow_blocked])
  1432. if blocks?(target, subscriber) and deny_follow_blocked do
  1433. {:error, "Could not subscribe: #{target.nickname} is blocking you"}
  1434. else
  1435. # Note: the relationship is inverse: subscriber acts as relationship target
  1436. UserRelationship.create_inverse_subscription(target, subscriber)
  1437. end
  1438. end
  1439. def subscribe(%User{} = subscriber, %{ap_id: ap_id}) do
  1440. with %User{} = subscribee <- get_cached_by_ap_id(ap_id) do
  1441. subscribe(subscriber, subscribee)
  1442. end
  1443. end
  1444. def unsubscribe(%User{} = unsubscriber, %User{} = target) do
  1445. # Note: the relationship is inverse: subscriber acts as relationship target
  1446. UserRelationship.delete_inverse_subscription(target, unsubscriber)
  1447. end
  1448. def unsubscribe(%User{} = unsubscriber, %{ap_id: ap_id}) do
  1449. with %User{} = user <- get_cached_by_ap_id(ap_id) do
  1450. unsubscribe(unsubscriber, user)
  1451. end
  1452. end
  1453. def block(%User{} = blocker, %User{} = blocked) do
  1454. # sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213)
  1455. blocker =
  1456. if following?(blocker, blocked) do
  1457. {:ok, blocker, _} = unfollow(blocker, blocked)
  1458. blocker
  1459. else
  1460. blocker
  1461. end
  1462. # clear any requested follows from both sides as well
  1463. blocked =
  1464. case CommonAPI.reject_follow_request(blocked, blocker) do
  1465. {:ok, %User{} = updated_blocked} -> updated_blocked
  1466. nil -> blocked
  1467. end
  1468. blocker =
  1469. case CommonAPI.reject_follow_request(blocker, blocked) do
  1470. {:ok, %User{} = updated_blocker} -> updated_blocker
  1471. nil -> blocker
  1472. end
  1473. unsubscribe(blocked, blocker)
  1474. unfollowing_blocked = Config.get([:activitypub, :unfollow_blocked], true)
  1475. if unfollowing_blocked && following?(blocked, blocker), do: unfollow(blocked, blocker)
  1476. {:ok, blocker} = update_follower_count(blocker)
  1477. {:ok, blocker, _} = Participation.mark_all_as_read(blocker, blocked)
  1478. add_to_block(blocker, blocked)
  1479. end
  1480. # helper to handle the block given only an actor's AP id
  1481. def block(%User{} = blocker, %{ap_id: ap_id}) do
  1482. block(blocker, get_cached_by_ap_id(ap_id))
  1483. end
  1484. def unblock(%User{} = blocker, %User{} = blocked) do
  1485. remove_from_block(blocker, blocked)
  1486. end
  1487. # helper to handle the block given only an actor's AP id
  1488. def unblock(%User{} = blocker, %{ap_id: ap_id}) do
  1489. unblock(blocker, get_cached_by_ap_id(ap_id))
  1490. end
  1491. def endorse(%User{} = endorser, %User{} = target) do
  1492. with max_endorsed_users <- Pleroma.Config.get([:instance, :max_endorsed_users], 0),
  1493. endorsed_users <-
  1494. User.endorsed_users_relation(endorser)
  1495. |> Repo.aggregate(:count, :id) do
  1496. cond do
  1497. endorsed_users >= max_endorsed_users ->
  1498. {:error, "You have already pinned the maximum number of users"}
  1499. not following?(endorser, target) ->
  1500. {:error, "Could not endorse: You are not following #{target.nickname}"}
  1501. true ->
  1502. UserRelationship.create_endorsement(endorser, target)
  1503. end
  1504. end
  1505. end
  1506. def endorse(%User{} = endorser, %{ap_id: ap_id}) do
  1507. with %User{} = endorsed <- get_cached_by_ap_id(ap_id) do
  1508. endorse(endorser, endorsed)
  1509. end
  1510. end
  1511. def unendorse(%User{} = unendorser, %User{} = target) do
  1512. UserRelationship.delete_endorsement(unendorser, target)
  1513. end
  1514. def unendorse(%User{} = unendorser, %{ap_id: ap_id}) do
  1515. with %User{} = user <- get_cached_by_ap_id(ap_id) do
  1516. unendorse(unendorser, user)
  1517. end
  1518. end
  1519. def mutes?(nil, _), do: false
  1520. def mutes?(%User{} = user, %User{} = target), do: mutes_user?(user, target)
  1521. def mutes_user?(%User{} = user, %User{} = target) do
  1522. UserRelationship.mute_exists?(user, target)
  1523. end
  1524. @spec muted_notifications?(User.t() | nil, User.t() | map()) :: boolean()
  1525. def muted_notifications?(nil, _), do: false
  1526. def muted_notifications?(%User{} = user, %User{} = target),
  1527. do: UserRelationship.notification_mute_exists?(user, target)
  1528. def blocks?(nil, _), do: false
  1529. def blocks?(%User{} = user, %User{} = target) do
  1530. blocks_user?(user, target) ||
  1531. (blocks_domain?(user, target) and not User.following?(user, target))
  1532. end
  1533. def blocks_user?(%User{} = user, %User{} = target) do
  1534. UserRelationship.block_exists?(user, target)
  1535. end
  1536. def blocks_user?(_, _), do: false
  1537. def blocks_domain?(%User{} = user, %User{} = target) do
  1538. domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.domain_blocks)
  1539. %{host: host} = URI.parse(target.ap_id)
  1540. Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, host)
  1541. end
  1542. def blocks_domain?(_, _), do: false
  1543. def subscribed_to?(%User{} = user, %User{} = target) do
  1544. # Note: the relationship is inverse: subscriber acts as relationship target
  1545. UserRelationship.inverse_subscription_exists?(target, user)
  1546. end
  1547. def subscribed_to?(%User{} = user, %{ap_id: ap_id}) do
  1548. with %User{} = target <- get_cached_by_ap_id(ap_id) do
  1549. subscribed_to?(user, target)
  1550. end
  1551. end
  1552. def endorses?(%User{} = user, %User{} = target) do
  1553. UserRelationship.endorsement_exists?(user, target)
  1554. end
  1555. @doc """
  1556. Returns map of outgoing (blocked, muted etc.) relationships' user AP IDs by relation type.
  1557. E.g. `outgoing_relationships_ap_ids(user, [:block])` -> `%{block: ["https://some.site/users/userapid"]}`
  1558. """
  1559. @spec outgoing_relationships_ap_ids(User.t(), list(atom())) :: %{atom() => list(String.t())}
  1560. def outgoing_relationships_ap_ids(_user, []), do: %{}
  1561. def outgoing_relationships_ap_ids(nil, _relationship_types), do: %{}
  1562. def outgoing_relationships_ap_ids(%User{} = user, relationship_types)
  1563. when is_list(relationship_types) do
  1564. db_result =
  1565. user
  1566. |> assoc(:outgoing_relationships)
  1567. |> join(:inner, [user_rel], u in assoc(user_rel, :target))
  1568. |> where([user_rel, u], user_rel.relationship_type in ^relationship_types)
  1569. |> select([user_rel, u], [user_rel.relationship_type, fragment("array_agg(?)", u.ap_id)])
  1570. |> group_by([user_rel, u], user_rel.relationship_type)
  1571. |> Repo.all()
  1572. |> Enum.into(%{}, fn [k, v] -> {k, v} end)
  1573. Enum.into(
  1574. relationship_types,
  1575. %{},
  1576. fn rel_type -> {rel_type, db_result[rel_type] || []} end
  1577. )
  1578. end
  1579. def incoming_relationships_ungrouped_ap_ids(user, relationship_types, ap_ids \\ nil)
  1580. def incoming_relationships_ungrouped_ap_ids(_user, [], _ap_ids), do: []
  1581. def incoming_relationships_ungrouped_ap_ids(nil, _relationship_types, _ap_ids), do: []
  1582. def incoming_relationships_ungrouped_ap_ids(%User{} = user, relationship_types, ap_ids)
  1583. when is_list(relationship_types) do
  1584. user
  1585. |> assoc(:incoming_relationships)
  1586. |> join(:inner, [user_rel], u in assoc(user_rel, :source))
  1587. |> where([user_rel, u], user_rel.relationship_type in ^relationship_types)
  1588. |> maybe_filter_on_ap_id(ap_ids)
  1589. |> select([user_rel, u], u.ap_id)
  1590. |> distinct(true)
  1591. |> Repo.all()
  1592. end
  1593. defp maybe_filter_on_ap_id(query, ap_ids) when is_list(ap_ids) do
  1594. where(query, [user_rel, u], u.ap_id in ^ap_ids)
  1595. end
  1596. defp maybe_filter_on_ap_id(query, _ap_ids), do: query
  1597. def set_activation_async(user, status \\ true) do
  1598. BackgroundWorker.new(%{"op" => "user_activation", "user_id" => user.id, "status" => status})
  1599. |> Oban.insert()
  1600. end
  1601. @spec set_activation([User.t()], boolean()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
  1602. def set_activation(users, status) when is_list(users) do
  1603. Repo.transaction(fn ->
  1604. for user <- users do
  1605. {:ok, user} = set_activation(user, status)
  1606. user
  1607. end
  1608. end)
  1609. end
  1610. @spec set_activation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
  1611. def set_activation(%User{} = user, status) do
  1612. with {:ok, user} <- set_activation_status(user, status) do
  1613. user
  1614. |> get_followers()
  1615. |> Enum.filter(& &1.local)
  1616. |> Enum.each(&set_cache(update_following_count(&1)))
  1617. # Only update local user counts, remote will be update during the next pull.
  1618. user
  1619. |> get_friends()
  1620. |> Enum.filter(& &1.local)
  1621. |> Enum.each(&do_unfollow(user, &1))
  1622. {:ok, user}
  1623. end
  1624. end
  1625. def approve(users) when is_list(users) do
  1626. Repo.transaction(fn ->
  1627. Enum.map(users, fn user ->
  1628. with {:ok, user} <- approve(user), do: user
  1629. end)
  1630. end)
  1631. end
  1632. def approve(%User{is_approved: false} = user) do
  1633. with chg <- change(user, is_approved: true),
  1634. {:ok, user} <- update_and_set_cache(chg) do
  1635. post_register_action(user)
  1636. {:ok, user}
  1637. end
  1638. end
  1639. def approve(%User{} = user), do: {:ok, user}
  1640. def confirm(users) when is_list(users) do
  1641. Repo.transaction(fn ->
  1642. Enum.map(users, fn user ->
  1643. with {:ok, user} <- confirm(user), do: user
  1644. end)
  1645. end)
  1646. end
  1647. def confirm(%User{is_confirmed: false} = user) do
  1648. with chg <- confirmation_changeset(user, set_confirmation: true),
  1649. {:ok, user} <- update_and_set_cache(chg) do
  1650. post_register_action(user)
  1651. {:ok, user}
  1652. end
  1653. end
  1654. def confirm(%User{} = user), do: {:ok, user}
  1655. def set_suggestion(users, is_suggested) when is_list(users) do
  1656. Repo.transaction(fn ->
  1657. Enum.map(users, fn user ->
  1658. with {:ok, user} <- set_suggestion(user, is_suggested), do: user
  1659. end)
  1660. end)
  1661. end
  1662. def set_suggestion(%User{is_suggested: is_suggested} = user, is_suggested), do: {:ok, user}
  1663. def set_suggestion(%User{} = user, is_suggested) when is_boolean(is_suggested) do
  1664. user
  1665. |> change(is_suggested: is_suggested)
  1666. |> update_and_set_cache()
  1667. end
  1668. def update_notification_settings(%User{} = user, settings) do
  1669. user
  1670. |> cast(%{notification_settings: settings}, [])
  1671. |> cast_embed(:notification_settings)
  1672. |> validate_required([:notification_settings])
  1673. |> update_and_set_cache()
  1674. end
  1675. @spec purge_user_changeset(User.t()) :: Ecto.Changeset.t()
  1676. def purge_user_changeset(user) do
  1677. # "Right to be forgotten"
  1678. # https://gdpr.eu/right-to-be-forgotten/
  1679. change(user, %{
  1680. bio: "",
  1681. raw_bio: nil,
  1682. email: nil,
  1683. name: nil,
  1684. password_hash: nil,
  1685. avatar: %{},
  1686. tags: [],
  1687. last_refreshed_at: nil,
  1688. last_digest_emailed_at: nil,
  1689. banner: %{},
  1690. background: %{},
  1691. note_count: 0,
  1692. follower_count: 0,
  1693. following_count: 0,
  1694. is_locked: false,
  1695. password_reset_pending: false,
  1696. registration_reason: nil,
  1697. confirmation_token: nil,
  1698. domain_blocks: [],
  1699. is_active: false,
  1700. is_moderator: false,
  1701. is_admin: false,
  1702. mascot: nil,
  1703. emoji: %{},
  1704. pleroma_settings_store: %{},
  1705. fields: [],
  1706. raw_fields: [],
  1707. is_discoverable: false,
  1708. also_known_as: []
  1709. # id: preserved
  1710. # ap_id: preserved
  1711. # nickname: preserved
  1712. })
  1713. end
  1714. # Purge doesn't delete the user from the database.
  1715. # It just nulls all its fields and deactivates it.
  1716. # See `User.purge_user_changeset/1` above.
  1717. defp purge(%User{} = user) do
  1718. user
  1719. |> purge_user_changeset()
  1720. |> update_and_set_cache()
  1721. end
  1722. def delete(users) when is_list(users) do
  1723. for user <- users, do: delete(user)
  1724. end
  1725. def delete(%User{} = user) do
  1726. # Purge the user immediately
  1727. purge(user)
  1728. DeleteWorker.new(%{"op" => "delete_user", "user_id" => user.id})
  1729. |> Oban.insert()
  1730. end
  1731. # *Actually* delete the user from the DB
  1732. defp delete_from_db(%User{} = user) do
  1733. invalidate_cache(user)
  1734. Repo.delete(user)
  1735. end
  1736. # If the user never finalized their account, it's safe to delete them.
  1737. defp maybe_delete_from_db(%User{local: true, is_confirmed: false} = user),
  1738. do: delete_from_db(user)
  1739. defp maybe_delete_from_db(%User{local: true, is_approved: false} = user),
  1740. do: delete_from_db(user)
  1741. defp maybe_delete_from_db(user), do: {:ok, user}
  1742. def perform(:force_password_reset, user), do: force_password_reset(user)
  1743. @spec perform(atom(), User.t()) :: {:ok, User.t()}
  1744. def perform(:delete, %User{} = user) do
  1745. # Purge the user again, in case perform/2 is called directly
  1746. purge(user)
  1747. # Remove all relationships
  1748. user
  1749. |> get_followers()
  1750. |> Enum.each(fn follower ->
  1751. ActivityPub.unfollow(follower, user)
  1752. unfollow(follower, user)
  1753. end)
  1754. user
  1755. |> get_friends()
  1756. |> Enum.each(fn followed ->
  1757. ActivityPub.unfollow(user, followed)
  1758. unfollow(user, followed)
  1759. end)
  1760. delete_user_activities(user)
  1761. delete_notifications_from_user_activities(user)
  1762. delete_outgoing_pending_follow_requests(user)
  1763. maybe_delete_from_db(user)
  1764. end
  1765. def perform(:verify_fields_links, user) do
  1766. profile_urls = [user.ap_id]
  1767. fields =
  1768. user.raw_fields
  1769. |> Enum.map(&verify_field_link(&1, profile_urls))
  1770. changeset =
  1771. user
  1772. |> update_changeset(%{raw_fields: fields})
  1773. with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do
  1774. set_cache(user)
  1775. end
  1776. end
  1777. def perform(:set_activation_async, user, status), do: set_activation(user, status)
  1778. defp verify_field_link(field, profile_urls) do
  1779. verified_at =
  1780. with %{"value" => value} <- field,
  1781. {:verified_at, nil} <- {:verified_at, Map.get(field, "verified_at")},
  1782. %{scheme: scheme, userinfo: nil, host: host}
  1783. when not_empty_string(host) and scheme in ["http", "https"] <-
  1784. URI.parse(value),
  1785. {:not_idn, true} <-
  1786. {:not_idn, match?(^host, to_string(:idna.encode(to_charlist(host))))},
  1787. "me" <- Pleroma.Web.RelMe.maybe_put_rel_me(value, profile_urls) do
  1788. CommonUtils.to_masto_date(NaiveDateTime.utc_now())
  1789. else
  1790. {:verified_at, value} when not_empty_string(value) ->
  1791. value
  1792. _ ->
  1793. nil
  1794. end
  1795. Map.put(field, "verified_at", verified_at)
  1796. end
  1797. @spec external_users_query() :: Ecto.Query.t()
  1798. def external_users_query do
  1799. User.Query.build(%{
  1800. external: true,
  1801. active: true,
  1802. order_by: :id
  1803. })
  1804. end
  1805. @spec external_users(keyword()) :: [User.t()]
  1806. def external_users(opts \\ []) do
  1807. query =
  1808. external_users_query()
  1809. |> select([u], struct(u, [:id, :ap_id]))
  1810. query =
  1811. if opts[:max_id],
  1812. do: where(query, [u], u.id > ^opts[:max_id]),
  1813. else: query
  1814. query =
  1815. if opts[:limit],
  1816. do: limit(query, ^opts[:limit]),
  1817. else: query
  1818. Repo.all(query)
  1819. end
  1820. def delete_notifications_from_user_activities(%User{ap_id: ap_id}) do
  1821. Notification
  1822. |> join(:inner, [n], activity in assoc(n, :activity))
  1823. |> where([n, a], fragment("? = ?", a.actor, ^ap_id))
  1824. |> Repo.delete_all()
  1825. end
  1826. def delete_user_activities(%User{ap_id: ap_id} = user) do
  1827. ap_id
  1828. |> Activity.Queries.by_actor()
  1829. |> Repo.chunk_stream(50, :batches)
  1830. |> Stream.each(fn activities ->
  1831. Enum.each(activities, fn activity -> delete_activity(activity, user) end)
  1832. end)
  1833. |> Stream.run()
  1834. end
  1835. defp delete_activity(%{data: %{"type" => "Create", "object" => object}} = activity, user) do
  1836. with {_, %Object{}} <- {:find_object, Object.get_by_ap_id(object)},
  1837. {:ok, delete_data, _} <- Builder.delete(user, object) do
  1838. Pipeline.common_pipeline(delete_data, local: user.local)
  1839. else
  1840. {:find_object, nil} ->
  1841. # We have the create activity, but not the object, it was probably pruned.
  1842. # Insert a tombstone and try again
  1843. with {:ok, tombstone_data, _} <- Builder.tombstone(user.ap_id, object),
  1844. {:ok, _tombstone} <- Object.create(tombstone_data) do
  1845. delete_activity(activity, user)
  1846. end
  1847. e ->
  1848. Logger.error("Could not delete #{object} created by #{activity.data["ap_id"]}")
  1849. Logger.error("Error: #{inspect(e)}")
  1850. end
  1851. end
  1852. defp delete_activity(%{data: %{"type" => type}} = activity, user)
  1853. when type in ["Like", "Announce"] do
  1854. {:ok, undo, _} = Builder.undo(user, activity)
  1855. Pipeline.common_pipeline(undo, local: user.local)
  1856. end
  1857. defp delete_activity(_activity, _user), do: "Doing nothing"
  1858. defp delete_outgoing_pending_follow_requests(user) do
  1859. user
  1860. |> FollowingRelationship.outgoing_pending_follow_requests_query()
  1861. |> Repo.delete_all()
  1862. end
  1863. def html_filter_policy(%User{no_rich_text: true}) do
  1864. Pleroma.HTML.Scrubber.TwitterText
  1865. end
  1866. def html_filter_policy(_), do: Config.get([:markup, :scrub_policy])
  1867. def fetch_by_ap_id(ap_id), do: ActivityPub.make_user_from_ap_id(ap_id)
  1868. @spec get_or_fetch_by_ap_id(String.t()) :: {:ok, User.t()} | {:error, any()}
  1869. def get_or_fetch_by_ap_id(ap_id) do
  1870. with cached_user = %User{} <- get_cached_by_ap_id(ap_id),
  1871. _ <- maybe_refresh(cached_user) do
  1872. {:ok, cached_user}
  1873. else
  1874. _ -> fetch_by_ap_id(ap_id)
  1875. end
  1876. end
  1877. defp maybe_refresh(user) do
  1878. if needs_update?(user) do
  1879. UserRefreshWorker.new(%{"ap_id" => user.ap_id})
  1880. |> Oban.insert()
  1881. end
  1882. end
  1883. @doc """
  1884. Creates an internal service actor by URI if missing.
  1885. Optionally takes nickname for addressing.
  1886. """
  1887. @spec get_or_create_service_actor_by_ap_id(String.t(), String.t()) :: User.t() | nil
  1888. def get_or_create_service_actor_by_ap_id(uri, nickname) do
  1889. {_, user} =
  1890. case get_cached_by_ap_id(uri) do
  1891. nil ->
  1892. with {:error, %{errors: errors}} <- create_service_actor(uri, nickname) do
  1893. Logger.error("Cannot create service actor: #{uri}/.\n#{inspect(errors)}")
  1894. {:error, nil}
  1895. end
  1896. %User{invisible: false} = user ->
  1897. set_invisible(user)
  1898. user ->
  1899. {:ok, user}
  1900. end
  1901. user
  1902. end
  1903. @spec set_invisible(User.t()) :: {:ok, User.t()}
  1904. defp set_invisible(user) do
  1905. user
  1906. |> change(%{invisible: true})
  1907. |> update_and_set_cache()
  1908. end
  1909. @spec create_service_actor(String.t(), String.t()) ::
  1910. {:ok, User.t()} | {:error, Ecto.Changeset.t()}
  1911. defp create_service_actor(uri, nickname) do
  1912. %User{
  1913. invisible: true,
  1914. local: true,
  1915. ap_id: uri,
  1916. nickname: nickname,
  1917. follower_address: uri <> "/followers"
  1918. }
  1919. |> change
  1920. |> put_private_key()
  1921. |> unique_constraint(:nickname)
  1922. |> Repo.insert()
  1923. |> set_cache()
  1924. end
  1925. def public_key(%{public_key: public_key_pem}) when is_binary(public_key_pem) do
  1926. key =
  1927. public_key_pem
  1928. |> :public_key.pem_decode()
  1929. |> hd()
  1930. |> :public_key.pem_entry_decode()
  1931. {:ok, key}
  1932. end
  1933. def public_key(_), do: {:error, "key not found"}
  1934. def get_public_key_for_ap_id(ap_id) do
  1935. with %User{} = user <- get_cached_by_ap_id(ap_id),
  1936. {:ok, public_key} <- public_key(user) do
  1937. {:ok, public_key}
  1938. else
  1939. _ -> :error
  1940. end
  1941. end
  1942. @doc "Gets or fetch a user by uri or nickname."
  1943. @spec get_or_fetch(String.t()) :: {:ok, User.t()} | {:error, String.t()}
  1944. def get_or_fetch("http://" <> _host = uri), do: get_or_fetch_by_ap_id(uri)
  1945. def get_or_fetch("https://" <> _host = uri), do: get_or_fetch_by_ap_id(uri)
  1946. def get_or_fetch(nickname), do: get_or_fetch_by_nickname(nickname)
  1947. # wait a period of time and return newest version of the User structs
  1948. # this is because we have synchronous follow APIs and need to simulate them
  1949. # with an async handshake
  1950. def wait_and_refresh(_, %User{local: true} = a, %User{local: true} = b) do
  1951. with %User{} = a <- get_cached_by_id(a.id),
  1952. %User{} = b <- get_cached_by_id(b.id) do
  1953. {:ok, a, b}
  1954. else
  1955. nil -> :error
  1956. end
  1957. end
  1958. def wait_and_refresh(timeout, %User{} = a, %User{} = b) do
  1959. with :ok <- :timer.sleep(timeout),
  1960. %User{} = a <- get_cached_by_id(a.id),
  1961. %User{} = b <- get_cached_by_id(b.id) do
  1962. {:ok, a, b}
  1963. else
  1964. nil -> :error
  1965. end
  1966. end
  1967. def parse_bio(bio) when is_binary(bio) and bio != "" do
  1968. bio
  1969. |> CommonUtils.format_input("text/plain", mentions_format: :full)
  1970. |> elem(0)
  1971. end
  1972. def parse_bio(_), do: ""
  1973. def parse_bio(bio, user) when is_binary(bio) and bio != "" do
  1974. # TODO: get profile URLs other than user.ap_id
  1975. profile_urls = [user.ap_id]
  1976. bio
  1977. |> CommonUtils.format_input("text/plain",
  1978. mentions_format: :full,
  1979. rel: &RelMe.maybe_put_rel_me(&1, profile_urls)
  1980. )
  1981. |> elem(0)
  1982. end
  1983. def parse_bio(_, _), do: ""
  1984. def tag(user_identifiers, tags) when is_list(user_identifiers) do
  1985. Repo.transaction(fn ->
  1986. for user_identifier <- user_identifiers, do: tag(user_identifier, tags)
  1987. end)
  1988. end
  1989. def tag(nickname, tags) when is_binary(nickname),
  1990. do: tag(get_by_nickname(nickname), tags)
  1991. def tag(%User{} = user, tags),
  1992. do: update_tags(user, Enum.uniq((user.tags || []) ++ normalize_tags(tags)))
  1993. def untag(user_identifiers, tags) when is_list(user_identifiers) do
  1994. Repo.transaction(fn ->
  1995. for user_identifier <- user_identifiers, do: untag(user_identifier, tags)
  1996. end)
  1997. end
  1998. def untag(nickname, tags) when is_binary(nickname),
  1999. do: untag(get_by_nickname(nickname), tags)
  2000. def untag(%User{} = user, tags),
  2001. do: update_tags(user, (user.tags || []) -- normalize_tags(tags))
  2002. defp update_tags(%User{} = user, new_tags) do
  2003. {:ok, updated_user} =
  2004. user
  2005. |> change(%{tags: new_tags})
  2006. |> update_and_set_cache()
  2007. updated_user
  2008. end
  2009. defp normalize_tags(tags) do
  2010. [tags]
  2011. |> List.flatten()
  2012. |> Enum.map(&String.downcase/1)
  2013. end
  2014. defp local_nickname_regex do
  2015. if Config.get([:instance, :extended_nickname_format]) do
  2016. @extended_local_nickname_regex
  2017. else
  2018. @strict_local_nickname_regex
  2019. end
  2020. end
  2021. def local_nickname(nickname_or_mention) do
  2022. nickname_or_mention
  2023. |> full_nickname()
  2024. |> String.split("@")
  2025. |> hd()
  2026. end
  2027. def full_nickname(%User{} = user) do
  2028. if String.contains?(user.nickname, "@") do
  2029. user.nickname
  2030. else
  2031. host = Pleroma.Web.WebFinger.host()
  2032. user.nickname <> "@" <> host
  2033. end
  2034. end
  2035. def full_nickname(nickname_or_mention),
  2036. do: String.trim_leading(nickname_or_mention, "@")
  2037. def error_user(ap_id) do
  2038. %User{
  2039. name: ap_id,
  2040. ap_id: ap_id,
  2041. nickname: "erroruser@example.com",
  2042. inserted_at: NaiveDateTime.utc_now()
  2043. }
  2044. end
  2045. @spec all_superusers() :: [User.t()]
  2046. def all_superusers do
  2047. User.Query.build(%{super_users: true, local: true, is_active: true})
  2048. |> Repo.all()
  2049. end
  2050. @spec all_users_with_privilege(atom()) :: [User.t()]
  2051. def all_users_with_privilege(privilege) do
  2052. User.Query.build(%{is_privileged: privilege}) |> Repo.all()
  2053. end
  2054. def muting_reblogs?(%User{} = user, %User{} = target) do
  2055. UserRelationship.reblog_mute_exists?(user, target)
  2056. end
  2057. def showing_reblogs?(%User{} = user, %User{} = target) do
  2058. not muting_reblogs?(user, target)
  2059. end
  2060. @doc """
  2061. The function returns a query to get users with no activity for given interval of days.
  2062. Inactive users are those who didn't read any notification, or had any activity where
  2063. the user is the activity's actor, during `inactivity_threshold` days.
  2064. Deactivated users will not appear in this list.
  2065. ## Examples
  2066. iex> Pleroma.User.list_inactive_users()
  2067. %Ecto.Query{}
  2068. """
  2069. @spec list_inactive_users_query(integer()) :: Ecto.Query.t()
  2070. def list_inactive_users_query(inactivity_threshold \\ 7) do
  2071. negative_inactivity_threshold = -inactivity_threshold
  2072. now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
  2073. # Subqueries are not supported in `where` clauses, join gets too complicated.
  2074. has_read_notifications =
  2075. from(n in Pleroma.Notification,
  2076. where: n.seen == true,
  2077. group_by: n.id,
  2078. having: max(n.updated_at) > datetime_add(^now, ^negative_inactivity_threshold, "day"),
  2079. select: n.user_id
  2080. )
  2081. |> Pleroma.Repo.all()
  2082. from(u in Pleroma.User,
  2083. left_join: a in Pleroma.Activity,
  2084. on: u.ap_id == a.actor,
  2085. where: not is_nil(u.nickname),
  2086. where: u.is_active == ^true,
  2087. where: u.id not in ^has_read_notifications,
  2088. group_by: u.id,
  2089. having:
  2090. max(a.inserted_at) < datetime_add(^now, ^negative_inactivity_threshold, "day") or
  2091. is_nil(max(a.inserted_at))
  2092. )
  2093. end
  2094. @doc """
  2095. Enable or disable email notifications for user
  2096. ## Examples
  2097. iex> Pleroma.User.switch_email_notifications(Pleroma.User{email_notifications: %{"digest" => false}}, "digest", true)
  2098. Pleroma.User{email_notifications: %{"digest" => true}}
  2099. iex> Pleroma.User.switch_email_notifications(Pleroma.User{email_notifications: %{"digest" => true}}, "digest", false)
  2100. Pleroma.User{email_notifications: %{"digest" => false}}
  2101. """
  2102. @spec switch_email_notifications(t(), String.t(), boolean()) ::
  2103. {:ok, t()} | {:error, Ecto.Changeset.t()}
  2104. def switch_email_notifications(user, type, status) do
  2105. User.update_email_notifications(user, %{type => status})
  2106. end
  2107. @doc """
  2108. Set `last_digest_emailed_at` value for the user to current time
  2109. """
  2110. @spec touch_last_digest_emailed_at(t()) :: t()
  2111. def touch_last_digest_emailed_at(user) do
  2112. now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
  2113. {:ok, updated_user} =
  2114. user
  2115. |> change(%{last_digest_emailed_at: now})
  2116. |> update_and_set_cache()
  2117. updated_user
  2118. end
  2119. @spec set_confirmation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
  2120. def set_confirmation(%User{} = user, bool) do
  2121. user
  2122. |> confirmation_changeset(set_confirmation: bool)
  2123. |> update_and_set_cache()
  2124. end
  2125. def get_mascot(%{mascot: %{} = mascot}) when not is_nil(mascot) do
  2126. mascot
  2127. end
  2128. def get_mascot(%{mascot: mascot}) when is_nil(mascot) do
  2129. # use instance-default
  2130. config = Config.get([:assets, :mascots])
  2131. default_mascot = Config.get([:assets, :default_mascot])
  2132. mascot = Keyword.get(config, default_mascot)
  2133. %{
  2134. "id" => "default-mascot",
  2135. "url" => mascot[:url],
  2136. "preview_url" => mascot[:url],
  2137. "pleroma" => %{
  2138. "mime_type" => mascot[:mime_type]
  2139. }
  2140. }
  2141. end
  2142. def get_ap_ids_by_nicknames(nicknames) do
  2143. from(u in User,
  2144. where: u.nickname in ^nicknames,
  2145. order_by: fragment("array_position(?, ?)", ^nicknames, u.nickname),
  2146. select: u.ap_id
  2147. )
  2148. |> Repo.all()
  2149. end
  2150. defp put_password_hash(
  2151. %Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset
  2152. ) do
  2153. change(changeset, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password))
  2154. end
  2155. defp put_password_hash(changeset), do: changeset
  2156. def internal?(%User{nickname: nil}), do: true
  2157. def internal?(%User{local: true, nickname: "internal." <> _}), do: true
  2158. def internal?(_), do: false
  2159. # A hack because user delete activities have a fake id for whatever reason
  2160. # TODO: Get rid of this
  2161. def get_delivered_users_by_object_id("pleroma:fake_object_id"), do: []
  2162. def get_delivered_users_by_object_id(object_id) do
  2163. from(u in User,
  2164. inner_join: delivery in assoc(u, :deliveries),
  2165. where: delivery.object_id == ^object_id
  2166. )
  2167. |> Repo.all()
  2168. end
  2169. def change_email(user, email) do
  2170. user
  2171. |> cast(%{email: email}, [:email])
  2172. |> maybe_validate_required_email(false)
  2173. |> unique_constraint(:email)
  2174. |> validate_format(:email, @email_regex)
  2175. |> update_and_set_cache()
  2176. end
  2177. def alias_users(user) do
  2178. user.also_known_as
  2179. |> Enum.map(&User.get_cached_by_ap_id/1)
  2180. |> Enum.filter(fn user -> user != nil end)
  2181. end
  2182. def add_alias(user, new_alias_user) do
  2183. current_aliases = user.also_known_as || []
  2184. new_alias_ap_id = new_alias_user.ap_id
  2185. if new_alias_ap_id in current_aliases do
  2186. {:ok, user}
  2187. else
  2188. user
  2189. |> cast(%{also_known_as: current_aliases ++ [new_alias_ap_id]}, [:also_known_as])
  2190. |> update_and_set_cache()
  2191. end
  2192. end
  2193. def delete_alias(user, alias_user) do
  2194. current_aliases = user.also_known_as || []
  2195. alias_ap_id = alias_user.ap_id
  2196. if alias_ap_id in current_aliases do
  2197. user
  2198. |> cast(%{also_known_as: current_aliases -- [alias_ap_id]}, [:also_known_as])
  2199. |> update_and_set_cache()
  2200. else
  2201. {:error, :no_such_alias}
  2202. end
  2203. end
  2204. # Internal function; public one is `deactivate/2`
  2205. defp set_activation_status(user, status) do
  2206. user
  2207. |> cast(%{is_active: status}, [:is_active])
  2208. |> update_and_set_cache()
  2209. end
  2210. def update_banner(user, banner) do
  2211. user
  2212. |> cast(%{banner: banner}, [:banner])
  2213. |> update_and_set_cache()
  2214. end
  2215. def update_background(user, background) do
  2216. user
  2217. |> cast(%{background: background}, [:background])
  2218. |> update_and_set_cache()
  2219. end
  2220. def validate_fields(changeset, remote? \\ false) do
  2221. limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
  2222. limit = Config.get([:instance, limit_name], 0)
  2223. changeset
  2224. |> validate_length(:fields, max: limit)
  2225. |> validate_change(:fields, fn :fields, fields ->
  2226. if Enum.all?(fields, &valid_field?/1) do
  2227. []
  2228. else
  2229. [fields: "invalid"]
  2230. end
  2231. end)
  2232. end
  2233. defp valid_field?(%{"name" => name, "value" => value}) do
  2234. name_limit = Config.get([:instance, :account_field_name_length], 255)
  2235. value_limit = Config.get([:instance, :account_field_value_length], 255)
  2236. is_binary(name) && is_binary(value) && String.length(name) <= name_limit &&
  2237. String.length(value) <= value_limit
  2238. end
  2239. defp valid_field?(_), do: false
  2240. defp truncate_field(%{"name" => name, "value" => value}) do
  2241. {name, _chopped} =
  2242. String.split_at(name, Config.get([:instance, :account_field_name_length], 255))
  2243. {value, _chopped} =
  2244. String.split_at(value, Config.get([:instance, :account_field_value_length], 255))
  2245. %{"name" => name, "value" => value}
  2246. end
  2247. def admin_api_update(user, params) do
  2248. user
  2249. |> cast(params, [
  2250. :is_moderator,
  2251. :is_admin,
  2252. :show_role
  2253. ])
  2254. |> update_and_set_cache()
  2255. end
  2256. @doc "Signs user out of all applications"
  2257. def global_sign_out(user) do
  2258. OAuth.Authorization.delete_user_authorizations(user)
  2259. OAuth.Token.delete_user_tokens(user)
  2260. end
  2261. def mascot_update(user, url) do
  2262. user
  2263. |> cast(%{mascot: url}, [:mascot])
  2264. |> validate_required([:mascot])
  2265. |> update_and_set_cache()
  2266. end
  2267. @spec confirmation_changeset(User.t(), keyword()) :: Ecto.Changeset.t()
  2268. def confirmation_changeset(user, set_confirmation: confirmed?) do
  2269. params =
  2270. if confirmed? do
  2271. %{
  2272. is_confirmed: true,
  2273. confirmation_token: nil
  2274. }
  2275. else
  2276. %{
  2277. is_confirmed: false,
  2278. confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64()
  2279. }
  2280. end
  2281. cast(user, params, [:is_confirmed, :confirmation_token])
  2282. end
  2283. @spec approval_changeset(Ecto.Changeset.t(), keyword()) :: Ecto.Changeset.t()
  2284. def approval_changeset(changeset, set_approval: approved?) do
  2285. cast(changeset, %{is_approved: approved?}, [:is_approved])
  2286. end
  2287. @spec add_pinned_object_id(User.t(), String.t()) :: {:ok, User.t()} | {:error, term()}
  2288. def add_pinned_object_id(%User{} = user, object_id) do
  2289. if !user.pinned_objects[object_id] do
  2290. params = %{pinned_objects: Map.put(user.pinned_objects, object_id, NaiveDateTime.utc_now())}
  2291. user
  2292. |> cast(params, [:pinned_objects])
  2293. |> validate_change(:pinned_objects, fn :pinned_objects, pinned_objects ->
  2294. max_pinned_statuses = Config.get([:instance, :max_pinned_statuses], 0)
  2295. if Enum.count(pinned_objects) <= max_pinned_statuses do
  2296. []
  2297. else
  2298. [pinned_objects: "You have already pinned the maximum number of statuses"]
  2299. end
  2300. end)
  2301. else
  2302. change(user)
  2303. end
  2304. |> update_and_set_cache()
  2305. end
  2306. @spec remove_pinned_object_id(User.t(), String.t()) :: {:ok, t()} | {:error, term()}
  2307. def remove_pinned_object_id(%User{} = user, object_id) do
  2308. user
  2309. |> cast(
  2310. %{pinned_objects: Map.delete(user.pinned_objects, object_id)},
  2311. [:pinned_objects]
  2312. )
  2313. |> update_and_set_cache()
  2314. end
  2315. def update_email_notifications(user, settings) do
  2316. email_notifications =
  2317. user.email_notifications
  2318. |> Map.merge(settings)
  2319. |> Map.take(["digest"])
  2320. params = %{email_notifications: email_notifications}
  2321. fields = [:email_notifications]
  2322. user
  2323. |> cast(params, fields)
  2324. |> validate_required(fields)
  2325. |> update_and_set_cache()
  2326. end
  2327. defp set_domain_blocks(user, domain_blocks) do
  2328. params = %{domain_blocks: domain_blocks}
  2329. user
  2330. |> cast(params, [:domain_blocks])
  2331. |> validate_required([:domain_blocks])
  2332. |> update_and_set_cache()
  2333. end
  2334. def block_domain(user, domain_blocked) do
  2335. set_domain_blocks(user, Enum.uniq([domain_blocked | user.domain_blocks]))
  2336. end
  2337. def unblock_domain(user, domain_blocked) do
  2338. set_domain_blocks(user, List.delete(user.domain_blocks, domain_blocked))
  2339. end
  2340. @spec add_to_block(User.t(), User.t()) ::
  2341. {:ok, UserRelationship.t()} | {:error, Ecto.Changeset.t()}
  2342. defp add_to_block(%User{} = user, %User{} = blocked) do
  2343. with {:ok, relationship} <- UserRelationship.create_block(user, blocked) do
  2344. @cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
  2345. {:ok, relationship}
  2346. end
  2347. end
  2348. @spec remove_from_block(User.t(), User.t()) ::
  2349. {:ok, UserRelationship.t()} | {:ok, nil} | {:error, Ecto.Changeset.t()}
  2350. defp remove_from_block(%User{} = user, %User{} = blocked) do
  2351. with {:ok, relationship} <- UserRelationship.delete_block(user, blocked) do
  2352. @cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
  2353. {:ok, relationship}
  2354. end
  2355. end
  2356. def set_invisible(user, invisible) do
  2357. params = %{invisible: invisible}
  2358. user
  2359. |> cast(params, [:invisible])
  2360. |> validate_required([:invisible])
  2361. |> update_and_set_cache()
  2362. end
  2363. def sanitize_html(%User{} = user) do
  2364. sanitize_html(user, nil)
  2365. end
  2366. # User data that mastodon isn't filtering (treated as plaintext):
  2367. # - field name
  2368. # - display name
  2369. def sanitize_html(%User{} = user, filter) do
  2370. fields =
  2371. Enum.map(user.fields, fn %{"name" => name, "value" => value} = fields ->
  2372. %{
  2373. "name" => name,
  2374. "value" => HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly),
  2375. "verified_at" => Map.get(fields, "verified_at")
  2376. }
  2377. end)
  2378. user
  2379. |> Map.put(:bio, HTML.filter_tags(user.bio, filter))
  2380. |> Map.put(:fields, fields)
  2381. end
  2382. def get_host(%User{ap_id: ap_id} = _user) do
  2383. URI.parse(ap_id).host
  2384. end
  2385. def update_last_active_at(%__MODULE__{local: true} = user) do
  2386. user
  2387. |> cast(%{last_active_at: NaiveDateTime.utc_now()}, [:last_active_at])
  2388. |> update_and_set_cache()
  2389. end
  2390. def update_last_active_at(user), do: user
  2391. def active_user_count(days \\ 30) do
  2392. active_after = Timex.shift(NaiveDateTime.utc_now(), days: -days)
  2393. __MODULE__
  2394. |> where([u], u.last_active_at >= ^active_after)
  2395. |> where([u], u.local == true)
  2396. |> Repo.aggregate(:count)
  2397. end
  2398. def update_last_status_at(user) do
  2399. User
  2400. |> where(id: ^user.id)
  2401. |> update([u], set: [last_status_at: fragment("NOW()")])
  2402. |> select([u], u)
  2403. |> Repo.update_all([])
  2404. |> case do
  2405. {1, [user]} -> set_cache(user)
  2406. _ -> {:error, user}
  2407. end
  2408. end
  2409. def get_friends_birthdays_query(%User{} = user, day, month) do
  2410. User.Query.build(%{
  2411. friends: user,
  2412. deactivated: false,
  2413. birthday_day: day,
  2414. birthday_month: month
  2415. })
  2416. end
  2417. defp maybe_load_followed_hashtags(%User{followed_hashtags: follows} = user)
  2418. when is_list(follows),
  2419. do: user
  2420. defp maybe_load_followed_hashtags(%User{} = user) do
  2421. followed_hashtags = HashtagFollow.get_by_user(user)
  2422. %{user | followed_hashtags: followed_hashtags}
  2423. end
  2424. def followed_hashtags(%User{followed_hashtags: follows})
  2425. when is_list(follows),
  2426. do: follows
  2427. def followed_hashtags(%User{} = user) do
  2428. {:ok, user} =
  2429. user
  2430. |> maybe_load_followed_hashtags()
  2431. |> set_cache()
  2432. user.followed_hashtags
  2433. end
  2434. def follow_hashtag(%User{} = user, %Hashtag{} = hashtag) do
  2435. Logger.debug("Follow hashtag #{hashtag.name} for user #{user.nickname}")
  2436. user = maybe_load_followed_hashtags(user)
  2437. with {:ok, _} <- HashtagFollow.new(user, hashtag),
  2438. follows <- HashtagFollow.get_by_user(user),
  2439. %User{} = user <- user |> Map.put(:followed_hashtags, follows) do
  2440. user
  2441. |> set_cache()
  2442. end
  2443. end
  2444. def unfollow_hashtag(%User{} = user, %Hashtag{} = hashtag) do
  2445. Logger.debug("Unfollow hashtag #{hashtag.name} for user #{user.nickname}")
  2446. user = maybe_load_followed_hashtags(user)
  2447. with {:ok, _} <- HashtagFollow.delete(user, hashtag),
  2448. follows <- HashtagFollow.get_by_user(user),
  2449. %User{} = user <- user |> Map.put(:followed_hashtags, follows) do
  2450. user
  2451. |> set_cache()
  2452. end
  2453. end
  2454. def following_hashtag?(%User{} = user, %Hashtag{} = hashtag) do
  2455. not is_nil(HashtagFollow.get(user, hashtag))
  2456. end
  2457. end