aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/api/admin_api.md59
-rw-r--r--docs/api/pleroma_api.md87
-rw-r--r--lib/mix/tasks/pleroma/user.ex83
-rw-r--r--lib/pleroma/notification.ex64
-rw-r--r--lib/pleroma/user.ex57
-rw-r--r--lib/pleroma/user/info.ex34
-rw-r--r--lib/pleroma/user_invite_token.ex113
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex16
-rw-r--r--lib/pleroma/web/admin_api/admin_api_controller.ex27
-rw-r--r--lib/pleroma/web/admin_api/views/account_view.ex18
-rw-r--r--lib/pleroma/web/common_api/utils.ex19
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex28
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex23
-rw-r--r--lib/pleroma/web/router.ex6
-rw-r--r--lib/pleroma/web/twitter_api/controllers/util_controller.ex9
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api.ex61
-rw-r--r--priv/repo/migrations/20190404050946_add_fields_to_user_invite_tokens.exs12
-rw-r--r--priv/repo/migrations/20190405160700_add_index_on_subscribers.exs8
-rw-r--r--test/fixtures/lambadalambda.json64
-rw-r--r--test/notification_test.exs103
-rw-r--r--test/support/http_request_mock.ex4
-rw-r--r--test/tasks/user_test.exs82
-rw-r--r--test/user_invite_token_test.exs96
-rw-r--r--test/user_test.exs25
-rw-r--r--test/web/admin_api/admin_api_controller_test.exs133
-rw-r--r--test/web/mastodon_api/account_view_test.exs16
-rw-r--r--test/web/mastodon_api/mastodon_api_controller_test.exs19
-rw-r--r--test/web/twitter_api/twitter_api_test.exs347
-rw-r--r--test/web/twitter_api/util_controller_test.exs42
29 files changed, 1520 insertions, 135 deletions
diff --git a/docs/api/admin_api.md b/docs/api/admin_api.md
index 86cacebb1..8befa8ea0 100644
--- a/docs/api/admin_api.md
+++ b/docs/api/admin_api.md
@@ -200,12 +200,65 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
## `/api/pleroma/admin/invite_token`
-### Get a account registeration invite token
+### Get an account registration invite token
- Methods: `GET`
-- Params: none
+- Params:
+ - *optional* `invite` => [
+ - *optional* `max_use` (integer)
+ - *optional* `expires_at` (date string e.g. "2019-04-07")
+ ]
- Response: invite token (base64 string)
+## `/api/pleroma/admin/invites`
+
+### Get a list of generated invites
+
+- Methods: `GET`
+- Params: none
+- Response:
+
+```JSON
+{
+
+ "invites": [
+ {
+ "id": integer,
+ "token": string,
+ "used": boolean,
+ "expires_at": date,
+ "uses": integer,
+ "max_use": integer,
+ "invite_type": string (possible values: `one_time`, `reusable`, `date_limited`, `reusable_date_limited`)
+ },
+ ...
+ ]
+}
+```
+
+## `/api/pleroma/admin/revoke_invite`
+
+### Revoke invite by token
+
+- Methods: `POST`
+- Params:
+ - `token`
+- Response:
+
+```JSON
+{
+ "id": integer,
+ "token": string,
+ "used": boolean,
+ "expires_at": date,
+ "uses": integer,
+ "max_use": integer,
+ "invite_type": string (possible values: `one_time`, `reusable`, `date_limited`, `reusable_date_limited`)
+
+}
+```
+
+
## `/api/pleroma/admin/email_invite`
### Sends registration invite via email
@@ -213,7 +266,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
- Methods: `POST`
- Params:
- `email`
- - `name`, optionnal
+ - `name`, optional
## `/api/pleroma/admin/password_reset`
diff --git a/docs/api/pleroma_api.md b/docs/api/pleroma_api.md
index 2e8fb04d2..dbe250300 100644
--- a/docs/api/pleroma_api.md
+++ b/docs/api/pleroma_api.md
@@ -10,7 +10,29 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi
* Authentication: not required
* Params: none
* Response: JSON
-* Example response: `[{"kalsarikannit_f":{"tags":["Finmoji"],"image_url":"/finmoji/128px/kalsarikannit_f-128.png"}},{"perkele":{"tags":["Finmoji"],"image_url":"/finmoji/128px/perkele-128.png"}},{"blobdab":{"tags":["SomeTag"],"image_url":"/emoji/blobdab.png"}},"happiness":{"tags":["Finmoji"],"image_url":"/finmoji/128px/happiness-128.png"}}]`
+* Example response:
+```json
+{
+ "girlpower": {
+ "tags": [
+ "Finmoji"
+ ],
+ "image_url": "/finmoji/128px/girlpower-128.png"
+ },
+ "education": {
+ "tags": [
+ "Finmoji"
+ ],
+ "image_url": "/finmoji/128px/education-128.png"
+ },
+ "finnishlove": {
+ "tags": [
+ "Finmoji"
+ ],
+ "image_url": "/finmoji/128px/finnishlove-128.png"
+ }
+}
+```
* Note: Same data as Mastodon API’s `/api/v1/custom_emojis` but in a different format
## `/api/pleroma/follow_import`
@@ -52,7 +74,7 @@ Request parameters can be passed via [query strings](https://en.wikipedia.org/wi
* `confirm`
* `captcha_solution`: optional, contains provider-specific captcha solution,
* `captcha_token`: optional, contains provider-specific captcha token
- * `token`: invite token required when the registerations aren't public.
+ * `token`: invite token required when the registrations aren't public.
* Response: JSON. Returns a user object on success, otherwise returns `{"error": "error_msg"}`
* Example response:
```
@@ -114,5 +136,64 @@ See [Admin-API](Admin-API.md)
* Method `POST`
* Authentication: required
* Params:
- * `id`: notifications's id
+ * `id`: notification's id
* Response: JSON. Returns `{"status": "success"}` if the reading was successful, otherwise returns `{"error": "error_msg"}`
+
+## `/api/v1/pleroma/accounts/:id/subscribe`
+### Subscribe to receive notifications for all statuses posted by a user
+* Method `POST`
+* Authentication: required
+* Params:
+ * `id`: account id to subscribe to
+* Response: JSON, returns a mastodon relationship object on success, otherwise returns `{"error": "error_msg"}`
+* Example response:
+```json
+{
+ "id": "abcdefg",
+ "following": true,
+ "followed_by": false,
+ "blocking": false,
+ "muting": false,
+ "muting_notifications": false,
+ "subscribing": true,
+ "requested": false,
+ "domain_blocking": false,
+ "showing_reblogs": true,
+ "endorsed": false
+}
+```
+
+## `/api/v1/pleroma/accounts/:id/unsubscribe`
+### Unsubscribe to stop receiving notifications from user statuses
+* Method `POST`
+* Authentication: required
+* Params:
+ * `id`: account id to unsubscribe from
+* Response: JSON, returns a mastodon relationship object on success, otherwise returns `{"error": "error_msg"}`
+* Example response:
+```json
+{
+ "id": "abcdefg",
+ "following": true,
+ "followed_by": false,
+ "blocking": false,
+ "muting": false,
+ "muting_notifications": false,
+ "subscribing": false,
+ "requested": false,
+ "domain_blocking": false,
+ "showing_reblogs": true,
+ "endorsed": false
+}
+```
+
+## `/api/pleroma/notification_settings`
+### Updates user notification settings
+* Method `PUT`
+* Authentication: required
+* Params:
+ * `followers`: BOOLEAN field, receives notifications from followers
+ * `follows`: BOOLEAN field, receives notifications from people the user follows
+ * `remote`: BOOLEAN field, receives notifications from people on remote instances
+ * `local`: BOOLEAN field, receives notifications from people on the local instance
+* Response: JSON. Returns `{"status": "success"}` if the update was successful, otherwise returns `{"error": "error_msg"}`
diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex
index 0d0bea8c0..441168df2 100644
--- a/lib/mix/tasks/pleroma/user.ex
+++ b/lib/mix/tasks/pleroma/user.ex
@@ -7,6 +7,7 @@ defmodule Mix.Tasks.Pleroma.User do
import Ecto.Changeset
alias Mix.Tasks.Pleroma.Common
alias Pleroma.User
+ alias Pleroma.UserInviteToken
@shortdoc "Manages Pleroma users"
@moduledoc """
@@ -26,7 +27,19 @@ defmodule Mix.Tasks.Pleroma.User do
## Generate an invite link.
- mix pleroma.user invite
+ mix pleroma.user invite [OPTION...]
+
+ Options:
+ - `--expires_at DATE` - last day on which token is active (e.g. "2019-04-05")
+ - `--max_use NUMBER` - maximum numbers of token uses
+
+ ## List generated invites
+
+ mix pleroma.user invites
+
+ ## Revoke invite
+
+ mix pleroma.user revoke_invite TOKEN OR TOKEN_ID
## Delete the user's account.
@@ -287,23 +300,79 @@ defmodule Mix.Tasks.Pleroma.User do
end
end
- def run(["invite"]) do
+ def run(["invite" | rest]) do
+ {options, [], []} =
+ OptionParser.parse(rest,
+ strict: [
+ expires_at: :string,
+ max_use: :integer
+ ]
+ )
+
+ options =
+ options
+ |> Keyword.update(:expires_at, {:ok, nil}, fn
+ nil -> {:ok, nil}
+ val -> Date.from_iso8601(val)
+ end)
+ |> Enum.into(%{})
+
Common.start_pleroma()
- with {:ok, token} <- Pleroma.UserInviteToken.create_token() do
- Mix.shell().info("Generated user invite token")
+ with {:ok, val} <- options[:expires_at],
+ options = Map.put(options, :expires_at, val),
+ {:ok, invite} <- UserInviteToken.create_invite(options) do
+ Mix.shell().info(
+ "Generated user invite token " <> String.replace(invite.invite_type, "_", " ")
+ )
url =
Pleroma.Web.Router.Helpers.redirect_url(
Pleroma.Web.Endpoint,
:registration_page,
- token.token
+ invite.token
)
IO.puts(url)
else
- _ ->
- Mix.shell().error("Could not create invite token.")
+ error ->
+ Mix.shell().error("Could not create invite token: #{inspect(error)}")
+ end
+ end
+
+ def run(["invites"]) do
+ Common.start_pleroma()
+
+ Mix.shell().info("Invites list:")
+
+ UserInviteToken.list_invites()
+ |> Enum.each(fn invite ->
+ expire_info =
+ with expires_at when not is_nil(expires_at) <- invite.expires_at do
+ " | Expires at: #{Date.to_string(expires_at)}"
+ end
+
+ using_info =
+ with max_use when not is_nil(max_use) <- invite.max_use do
+ " | Max use: #{max_use} Left use: #{max_use - invite.uses}"
+ end
+
+ Mix.shell().info(
+ "ID: #{invite.id} | Token: #{invite.token} | Token type: #{invite.invite_type} | Used: #{
+ invite.used
+ }#{expire_info}#{using_info}"
+ )
+ end)
+ end
+
+ def run(["revoke_invite", token]) do
+ Common.start_pleroma()
+
+ with {:ok, invite} <- UserInviteToken.find_by_token(token),
+ {:ok, _} <- UserInviteToken.update_invite(invite, %{used: true}) do
+ Mix.shell().info("Invite for token #{token} was revoked.")
+ else
+ _ -> Mix.shell().error("No invite found with token #{token}")
end
end
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index cac10f24a..15789907a 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -122,13 +122,7 @@ defmodule Pleroma.Notification do
# TODO move to sql, too.
def create_notification(%Activity{} = activity, %User{} = user) do
- unless User.blocks?(user, %{ap_id: activity.data["actor"]}) or
- CommonAPI.thread_muted?(user, activity) or user.ap_id == activity.data["actor"] or
- (activity.data["type"] == "Follow" and
- Enum.any?(Notification.for_user(user), fn notif ->
- notif.activity.data["type"] == "Follow" and
- notif.activity.data["actor"] == activity.data["actor"]
- end)) do
+ unless skip?(activity, user) do
notification = %Notification{user_id: user.id, activity: activity}
{:ok, notification} = Repo.insert(notification)
Pleroma.Web.Streamer.stream("user", notification)
@@ -148,10 +142,66 @@ defmodule Pleroma.Notification do
[]
|> Utils.maybe_notify_to_recipients(activity)
|> Utils.maybe_notify_mentioned_recipients(activity)
+ |> Utils.maybe_notify_subscribers(activity)
|> Enum.uniq()
User.get_users_from_set(recipients, local_only)
end
def get_notified_from_activity(_, _local_only), do: []
+
+ def skip?(activity, user) do
+ [:self, :blocked, :local, :muted, :followers, :follows, :recently_followed]
+ |> Enum.any?(&skip?(&1, activity, user))
+ end
+
+ def skip?(:self, activity, user) do
+ activity.data["actor"] == user.ap_id
+ end
+
+ def skip?(:blocked, activity, user) do
+ actor = activity.data["actor"]
+ User.blocks?(user, %{ap_id: actor})
+ end
+
+ def skip?(:local, %{local: true}, %{info: %{notification_settings: %{"local" => false}}}),
+ do: true
+
+ def skip?(:local, %{local: false}, %{info: %{notification_settings: %{"remote" => false}}}),
+ do: true
+
+ def skip?(:muted, activity, user) do
+ actor = activity.data["actor"]
+
+ User.mutes?(user, %{ap_id: actor}) or
+ CommonAPI.thread_muted?(user, activity)
+ end
+
+ def skip?(
+ :followers,
+ activity,
+ %{info: %{notification_settings: %{"followers" => false}}} = user
+ ) do
+ actor = activity.data["actor"]
+ follower = User.get_cached_by_ap_id(actor)
+ User.following?(follower, user)
+ end
+
+ def skip?(:follows, activity, %{info: %{notification_settings: %{"follows" => false}}} = user) do
+ actor = activity.data["actor"]
+ followed = User.get_by_ap_id(actor)
+ User.following?(user, followed)
+ end
+
+ def skip?(:recently_followed, %{data: %{"type" => "Follow"}} = activity, user) do
+ actor = activity.data["actor"]
+
+ Notification.for_user(user)
+ |> Enum.any?(fn
+ %{activity: %{data: %{"type" => "Follow", "actor" => ^actor}}} -> true
+ _ -> false
+ end)
+ end
+
+ def skip?(_, _, _), do: false
end
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 4f579b597..6e2269aff 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -931,6 +931,38 @@ defmodule Pleroma.User do
update_and_set_cache(cng)
end
+ def subscribe(subscriber, %{ap_id: ap_id}) do
+ deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
+
+ with %User{} = subscribed <- get_cached_by_ap_id(ap_id) do
+ blocked = blocks?(subscribed, subscriber) and deny_follow_blocked
+
+ if blocked do
+ {:error, "Could not subscribe: #{subscribed.nickname} is blocking you"}
+ else
+ info_cng =
+ subscribed.info
+ |> User.Info.add_to_subscribers(subscriber.ap_id)
+
+ change(subscribed)
+ |> put_embed(:info, info_cng)
+ |> update_and_set_cache()
+ end
+ end
+ end
+
+ def unsubscribe(unsubscriber, %{ap_id: ap_id}) do
+ with %User{} = user <- get_cached_by_ap_id(ap_id) do
+ info_cng =
+ user.info
+ |> User.Info.remove_from_subscribers(unsubscriber.ap_id)
+
+ change(user)
+ |> put_embed(:info, info_cng)
+ |> update_and_set_cache()
+ end
+ end
+
def block(blocker, %User{ap_id: ap_id} = blocked) do
# sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213)
blocker =
@@ -941,6 +973,14 @@ defmodule Pleroma.User do
blocker
end
+ blocker =
+ if subscribed_to?(blocked, blocker) do
+ {:ok, blocker} = unsubscribe(blocked, blocker)
+ blocker
+ else
+ blocker
+ end
+
if following?(blocked, blocker) do
unfollow(blocked, blocker)
end
@@ -989,12 +1029,21 @@ defmodule Pleroma.User do
end)
end
+ def subscribed_to?(user, %{ap_id: ap_id}) do
+ with %User{} = target <- User.get_by_ap_id(ap_id) do
+ Enum.member?(target.info.subscribers, user.ap_id)
+ end
+ end
+
def muted_users(user),
do: Repo.all(from(u in User, where: u.ap_id in ^user.info.mutes))
def blocked_users(user),
do: Repo.all(from(u in User, where: u.ap_id in ^user.info.blocks))
+ def subscribers(user),
+ do: Repo.all(from(u in User, where: u.ap_id in ^user.info.subscribers))
+
def block_domain(user, domain) do
info_cng =
user.info
@@ -1092,6 +1141,14 @@ defmodule Pleroma.User do
update_and_set_cache(cng)
end
+ def update_notification_settings(%User{} = user, settings \\ %{}) do
+ info_changeset = User.Info.update_notification_settings(user.info, settings)
+
+ change(user)
+ |> put_embed(:info, info_changeset)
+ |> update_and_set_cache()
+ end
+
def delete(%User{} = user) do
{:ok, user} = User.deactivate(user)
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index 740a46727..5afa7988c 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -22,6 +22,7 @@ defmodule Pleroma.User.Info do
field(:domain_blocks, {:array, :string}, default: [])
field(:mutes, {:array, :string}, default: [])
field(:muted_reblogs, {:array, :string}, default: [])
+ field(:subscribers, {:array, :string}, default: [])
field(:deactivated, :boolean, default: false)
field(:no_rich_text, :boolean, default: false)
field(:ap_enabled, :boolean, default: false)
@@ -40,6 +41,10 @@ defmodule Pleroma.User.Info do
field(:pinned_activities, {:array, :string}, default: [])
field(:flavour, :string, default: nil)
+ field(:notification_settings, :map,
+ default: %{"remote" => true, "local" => true, "followers" => true, "follows" => true}
+ )
+
# Found in the wild
# ap_id -> Where is this used?
# bio -> Where is this used?
@@ -57,6 +62,19 @@ defmodule Pleroma.User.Info do
|> validate_required([:deactivated])
end
+ def update_notification_settings(info, settings) do
+ notification_settings =
+ info.notification_settings
+ |> Map.merge(settings)
+ |> Map.take(["remote", "local", "followers", "follows"])
+
+ params = %{notification_settings: notification_settings}
+
+ info
+ |> cast(params, [:notification_settings])
+ |> validate_required([:notification_settings])
+ end
+
def add_to_note_count(info, number) do
set_note_count(info, info.note_count + number)
end
@@ -93,6 +111,14 @@ defmodule Pleroma.User.Info do
|> validate_required([:blocks])
end
+ def set_subscribers(info, subscribers) do
+ params = %{subscribers: subscribers}
+
+ info
+ |> cast(params, [:subscribers])
+ |> validate_required([:subscribers])
+ end
+
def add_to_mutes(info, muted) do
set_mutes(info, Enum.uniq([muted | info.mutes]))
end
@@ -109,6 +135,14 @@ defmodule Pleroma.User.Info do
set_blocks(info, List.delete(info.blocks, blocked))
end
+ def add_to_subscribers(info, subscribed) do
+ set_subscribers(info, Enum.uniq([subscribed | info.subscribers]))
+ end
+
+ def remove_from_subscribers(info, subscribed) do
+ set_subscribers(info, List.delete(info.subscribers, subscribed))
+ end
+
def set_domain_blocks(info, domain_blocks) do
params = %{domain_blocks: domain_blocks}
diff --git a/lib/pleroma/user_invite_token.ex b/lib/pleroma/user_invite_token.ex
index 9c5579934..86f0a5486 100644
--- a/lib/pleroma/user_invite_token.ex
+++ b/lib/pleroma/user_invite_token.ex
@@ -6,40 +6,119 @@ defmodule Pleroma.UserInviteToken do
use Ecto.Schema
import Ecto.Changeset
-
+ import Ecto.Query
alias Pleroma.Repo
alias Pleroma.UserInviteToken
+ @type t :: %__MODULE__{}
+ @type token :: String.t()
+
schema "user_invite_tokens" do
field(:token, :string)
field(:used, :boolean, default: false)
+ field(:max_use, :integer)
+ field(:expires_at, :date)
+ field(:uses, :integer, default: 0)
+ field(:invite_type, :string)
timestamps()
end
- def create_token do
+ @spec create_invite(map()) :: UserInviteToken.t()
+ def create_invite(params \\ %{}) do
+ %UserInviteToken{}
+ |> cast(params, [:max_use, :expires_at])
+ |> add_token()
+ |> assign_type()
+ |> Repo.insert()
+ end
+
+ defp add_token(changeset) do
token = :crypto.strong_rand_bytes(32) |> Base.url_encode64()
+ put_change(changeset, :token, token)
+ end
- token = %UserInviteToken{
- used: false,
- token: token
- }
+ defp assign_type(%{changes: %{max_use: _max_use, expires_at: _expires_at}} = changeset) do
+ put_change(changeset, :invite_type, "reusable_date_limited")
+ end
+
+ defp assign_type(%{changes: %{expires_at: _expires_at}} = changeset) do
+ put_change(changeset, :invite_type, "date_limited")
+ end
+
+ defp assign_type(%{changes: %{max_use: _max_use}} = changeset) do
+ put_change(changeset, :invite_type, "reusable")
+ end
+
+ defp assign_type(changeset), do: put_change(changeset, :invite_type, "one_time")
- Repo.insert(token)
+ @spec list_invites() :: [UserInviteToken.t()]
+ def list_invites do
+ query = from(u in UserInviteToken, order_by: u.id)
+ Repo.all(query)
end
- def used_changeset(struct) do
- struct
- |> cast(%{}, [])
- |> put_change(:used, true)
+ @spec update_invite!(UserInviteToken.t(), map()) :: UserInviteToken.t() | no_return()
+ def update_invite!(invite, changes) do
+ change(invite, changes) |> Repo.update!()
end
- def mark_as_used(token) do
- with %{used: false} = token <- Repo.get_by(UserInviteToken, %{token: token}),
- {:ok, token} <- Repo.update(used_changeset(token)) do
- {:ok, token}
- else
- _e -> {:error, token}
+ @spec update_invite(UserInviteToken.t(), map()) ::
+ {:ok, UserInviteToken.t()} | {:error, Changeset.t()}
+ def update_invite(invite, changes) do
+ change(invite, changes) |> Repo.update()
+ end
+
+ @spec find_by_token!(token()) :: UserInviteToken.t() | no_return()
+ def find_by_token!(token), do: Repo.get_by!(UserInviteToken, token: token)
+
+ @spec find_by_token(token()) :: {:ok, UserInviteToken.t()} | nil
+ def find_by_token(token) do
+ with invite <- Repo.get_by(UserInviteToken, token: token) do
+ {:ok, invite}
end
end
+
+ @spec valid_invite?(UserInviteToken.t()) :: boolean()
+ def valid_invite?(%{invite_type: "one_time"} = invite) do
+ not invite.used
+ end
+
+ def valid_invite?(%{invite_type: "date_limited"} = invite) do
+ not_overdue_date?(invite) and not invite.used
+ end
+
+ def valid_invite?(%{invite_type: "reusable"} = invite) do
+ invite.uses < invite.max_use and not invite.used
+ end
+
+ def valid_invite?(%{invite_type: "reusable_date_limited"} = invite) do
+ not_overdue_date?(invite) and invite.uses < invite.max_use and not invite.used
+ end
+
+ defp not_overdue_date?(%{expires_at: expires_at}) do
+ Date.compare(Date.utc_today(), expires_at) in [:lt, :eq]
+ end
+
+ @spec update_usage!(UserInviteToken.t()) :: nil | UserInviteToken.t() | no_return()
+ def update_usage!(%{invite_type: "date_limited"}), do: nil
+
+ def update_usage!(%{invite_type: "one_time"} = invite),
+ do: update_invite!(invite, %{used: true})
+
+ def update_usage!(%{invite_type: invite_type} = invite)
+ when invite_type == "reusable" or invite_type == "reusable_date_limited" do
+ changes = %{
+ uses: invite.uses + 1
+ }
+
+ changes =
+ if changes.uses >= invite.max_use do
+ Map.put(changes, :used, true)
+ else
+ changes
+ end
+
+ update_invite!(invite, changes)
+ end
end
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 593ae3188..49ea73204 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -83,6 +83,22 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> fix_content_map
|> fix_likes
|> fix_addressing
+ |> fix_summary
+ end
+
+ def fix_summary(%{"summary" => nil} = object) do
+ object
+ |> Map.put("summary", "")
+ end
+
+ def fix_summary(%{"summary" => _} = object) do
+ # summary is present, nothing to do
+ object
+ end
+
+ def fix_summary(object) do
+ object
+ |> Map.put("summary", "")
end
def fix_addressing_list(map, field) do
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index 78bf31893..70a5b5c5d 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
use Pleroma.Web, :controller
alias Pleroma.User
+ alias Pleroma.UserInviteToken
alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.AdminAPI.AccountView
alias Pleroma.Web.AdminAPI.Search
@@ -235,7 +236,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
with true <-
Pleroma.Config.get([:instance, :invites_enabled]) &&
!Pleroma.Config.get([:instance, :registrations_open]),
- {:ok, invite_token} <- Pleroma.UserInviteToken.create_token(),
+ {:ok, invite_token} <- UserInviteToken.create_invite(),
email <-
Pleroma.UserEmail.user_invitation_email(user, invite_token, email, params["name"]),
{:ok, _} <- Pleroma.Mailer.deliver(email) do
@@ -244,11 +245,29 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end
@doc "Get a account registeration invite token (base64 string)"
- def get_invite_token(conn, _params) do
- {:ok, token} = Pleroma.UserInviteToken.create_token()
+ def get_invite_token(conn, params) do
+ options = params["invite"] || %{}
+ {:ok, invite} = UserInviteToken.create_invite(options)
conn
- |> json(token.token)
+ |> json(invite.token)
+ end
+
+ @doc "Get list of created invites"
+ def invites(conn, _params) do
+ invites = UserInviteToken.list_invites()
+
+ conn
+ |> json(AccountView.render("invites.json", %{invites: invites}))
+ end
+
+ @doc "Revokes invite by token"
+ def revoke_invite(conn, %{"token" => token}) do
+ invite = UserInviteToken.find_by_token!(token)
+ {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true})
+
+ conn
+ |> json(AccountView.render("invite.json", %{invite: updated_invite}))
end
@doc "Get a password reset token (base64 string) for given nickname"
diff --git a/lib/pleroma/web/admin_api/views/account_view.ex b/lib/pleroma/web/admin_api/views/account_view.ex
index 4d6f921ef..28bb667d8 100644
--- a/lib/pleroma/web/admin_api/views/account_view.ex
+++ b/lib/pleroma/web/admin_api/views/account_view.ex
@@ -26,4 +26,22 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
"tags" => user.tags || []
}
end
+
+ def render("invite.json", %{invite: invite}) do
+ %{
+ "id" => invite.id,
+ "token" => invite.token,
+ "used" => invite.used,
+ "expires_at" => invite.expires_at,
+ "uses" => invite.uses,
+ "max_use" => invite.max_use,
+ "invite_type" => invite.invite_type
+ }
+ end
+
+ def render("invites.json", %{invites: invites}) do
+ %{
+ invites: render_many(invites, AccountView, "invite.json", as: :invite)
+ }
+ end
end
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 051db6c79..7b9f0ea06 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -12,6 +12,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Utils
+ alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.Endpoint
alias Pleroma.Web.MediaProxy
@@ -335,6 +336,24 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def maybe_notify_mentioned_recipients(recipients, _), do: recipients
+ def maybe_notify_subscribers(
+ recipients,
+ %Activity{data: %{"actor" => actor, "type" => type}} = activity
+ )
+ when type == "Create" do
+ with %User{} = user <- User.get_cached_by_ap_id(actor) do
+ subscriber_ids =
+ user
+ |> User.subscribers()
+ |> Enum.filter(&Visibility.visible_for_user?(activity, &1))
+ |> Enum.map(& &1.ap_id)
+
+ recipients ++ subscriber_ids
+ end
+ end
+
+ def maybe_notify_subscribers(recipients, _), do: recipients
+
def maybe_extract_mentions(%{"tag" => tag}) do
tag
|> Enum.filter(fn x -> is_map(x) end)
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 5462ce8be..ed082abdf 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -931,6 +931,34 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
json(conn, %{})
end
+ def subscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ with %User{} = subscription_target <- User.get_cached_by_id(id),
+ {:ok, subscription_target} = User.subscribe(user, subscription_target) do
+ conn
+ |> put_view(AccountView)
+ |> render("relationship.json", %{user: user, target: subscription_target})
+ else
+ {:error, message} ->
+ conn
+ |> put_resp_content_type("application/json")
+ |> send_resp(403, Jason.encode!(%{"error" => message}))
+ end
+ end
+
+ def unsubscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ with %User{} = subscription_target <- User.get_cached_by_id(id),
+ {:ok, subscription_target} = User.unsubscribe(user, subscription_target) do
+ conn
+ |> put_view(AccountView)
+ |> render("relationship.json", %{user: user, target: subscription_target})
+ else
+ {:error, message} ->
+ conn
+ |> put_resp_content_type("application/json")
+ |> send_resp(403, Jason.encode!(%{"error" => message}))
+ end
+ end
+
def status_search(user, query) do
fetched =
if Regex.match?(~r/https?:/, query) do
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index b5f3bbb9d..af56c4149 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -53,6 +53,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
blocking: User.blocks?(user, target),
muting: User.mutes?(user, target),
muting_notifications: false,
+ subscribing: User.subscribed_to?(user, target),
requested: requested,
domain_blocking: false,
showing_reblogs: User.showing_reblogs?(user, target),
@@ -117,13 +118,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
},
# Pleroma extension
- pleroma: %{
- confirmation_pending: user_info.confirmation_pending,
- tags: user.tags,
- is_moderator: user.info.is_moderator,
- is_admin: user.info.is_admin,
- relationship: relationship
- }
+ pleroma:
+ %{
+ confirmation_pending: user_info.confirmation_pending,
+ tags: user.tags,
+ is_moderator: user.info.is_moderator,
+ is_admin: user.info.is_admin,
+ relationship: relationship
+ }
+ |> with_notification_settings(user, opts[:for])
}
end
@@ -132,4 +135,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
end
defp username_from_nickname(_), do: nil
+
+ defp with_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do
+ Map.put(data, :notification_settings, user.info.notification_settings)
+ end
+
+ defp with_notification_settings(data, _, _), do: data
end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index ef38fc34d..172f337db 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -168,6 +168,8 @@ defmodule Pleroma.Web.Router do
delete("/relay", AdminAPIController, :relay_unfollow)
get("/invite_token", AdminAPIController, :get_invite_token)
+ get("/invites", AdminAPIController, :invites)
+ post("/revoke_invite", AdminAPIController, :revoke_invite)
post("/email_invite", AdminAPIController, :email_invite)
get("/password_reset", AdminAPIController, :get_password_reset)
@@ -193,6 +195,7 @@ defmodule Pleroma.Web.Router do
post("/change_password", UtilController, :change_password)
post("/delete_account", UtilController, :delete_account)
+ put("/notification_settings", UtilController, :update_notificaton_settings)
end
scope [] do
@@ -336,6 +339,9 @@ defmodule Pleroma.Web.Router do
post("/domain_blocks", MastodonAPIController, :block_domain)
delete("/domain_blocks", MastodonAPIController, :unblock_domain)
+
+ post("/pleroma/accounts/:id/subscribe", MastodonAPIController, :subscribe)
+ post("/pleroma/accounts/:id/unsubscribe", MastodonAPIController, :unsubscribe)
end
scope [] do
diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
index 26407aebd..d066d35f5 100644
--- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
@@ -286,12 +286,19 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
emoji =
Emoji.get_all()
|> Enum.map(fn {short_code, path, tags} ->
- %{short_code => %{image_url: path, tags: String.split(tags, ",")}}
+ {short_code, %{image_url: path, tags: String.split(tags, ",")}}
end)
+ |> Enum.into(%{})
json(conn, emoji)
end
+ def update_notificaton_settings(%{assigns: %{user: user}} = conn, params) do
+ with {:ok, _} <- User.update_notification_settings(user, params) do
+ json(conn, %{status: "success"})
+ end
+ end
+
def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
follow_import(conn, %{"list" => File.read!(listfile.path)})
end
diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex
index 9b081a316..9e9a46cf1 100644
--- a/lib/pleroma/web/twitter_api/twitter_api.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api.ex
@@ -129,7 +129,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
end
def register_user(params) do
- token_string = params["token"]
+ token = params["token"]
params = %{
nickname: params["nickname"],
@@ -163,36 +163,49 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
{:error, %{error: Jason.encode!(%{captcha: [error]})}}
else
registrations_open = Pleroma.Config.get([:instance, :registrations_open])
+ registration_process(registrations_open, params, token)
+ end
+ end
- # no need to query DB if registration is open
- token =
- unless registrations_open || is_nil(token_string) do
- Repo.get_by(UserInviteToken, %{token: token_string})
- end
+ defp registration_process(registration_open, params, token)
+ when registration_open == false or is_nil(registration_open) do
+ invite =
+ unless is_nil(token) do
+ Repo.get_by(UserInviteToken, %{token: token})
+ end
- cond do
- registrations_open || (!is_nil(token) && !token.used) ->
- changeset = User.register_changeset(%User{}, params)
+ valid_invite? = invite && UserInviteToken.valid_invite?(invite)
- with {:ok, user} <- User.register(changeset) do
- !registrations_open && UserInviteToken.mark_as_used(token.token)
+ case invite do
+ nil ->
+ {:error, "Invalid token"}
- {:ok, user}
- else
- {:error, changeset} ->
- errors =
- Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end)
- |> Jason.encode!()
+ invite when valid_invite? ->
+ UserInviteToken.update_usage!(invite)
+ create_user(params)
- {:error, %{error: errors}}
- end
+ _ ->
+ {:error, "Expired token"}
+ end
+ end
- !registrations_open && is_nil(token) ->
- {:error, "Invalid token"}
+ defp registration_process(true, params, _token) do
+ create_user(params)
+ end
- !registrations_open && token.used ->
- {:error, "Expired token"}
- end
+ defp create_user(params) do
+ changeset = User.register_changeset(%User{}, params)
+
+ case User.register(changeset) do
+ {:ok, user} ->
+ {:ok, user}
+
+ {:error, changeset} ->
+ errors =
+ Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end)
+ |> Jason.encode!()
+
+ {:error, %{error: errors}}
end
end
diff --git a/priv/repo/migrations/20190404050946_add_fields_to_user_invite_tokens.exs b/priv/repo/migrations/20190404050946_add_fields_to_user_invite_tokens.exs
new file mode 100644
index 000000000..211a14135
--- /dev/null
+++ b/priv/repo/migrations/20190404050946_add_fields_to_user_invite_tokens.exs
@@ -0,0 +1,12 @@
+defmodule Pleroma.Repo.Migrations.AddFieldsToUserInviteTokens do
+ use Ecto.Migration
+
+ def change do
+ alter table(:user_invite_tokens) do
+ add(:expires_at, :date)
+ add(:uses, :integer, default: 0)
+ add(:max_use, :integer)
+ add(:invite_type, :string, default: "one_time")
+ end
+ end
+end
diff --git a/priv/repo/migrations/20190405160700_add_index_on_subscribers.exs b/priv/repo/migrations/20190405160700_add_index_on_subscribers.exs
new file mode 100644
index 000000000..232f75c92
--- /dev/null
+++ b/priv/repo/migrations/20190405160700_add_index_on_subscribers.exs
@@ -0,0 +1,8 @@
+defmodule Pleroma.Repo.Migrations.AddIndexOnSubscribers do
+ use Ecto.Migration
+
+ @disable_ddl_transaction true
+ def change do
+ create index(:users, ["(info->'subscribers')"], name: :users_subscribers_index, using: :gin, concurrently: true)
+ end
+end
diff --git a/test/fixtures/lambadalambda.json b/test/fixtures/lambadalambda.json
new file mode 100644
index 000000000..1f09fb591
--- /dev/null
+++ b/test/fixtures/lambadalambda.json
@@ -0,0 +1,64 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ {
+ "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+ "toot": "http://joinmastodon.org/ns#",
+ "featured": {
+ "@id": "toot:featured",
+ "@type": "@id"
+ },
+ "alsoKnownAs": {
+ "@id": "as:alsoKnownAs",
+ "@type": "@id"
+ },
+ "movedTo": {
+ "@id": "as:movedTo",
+ "@type": "@id"
+ },
+ "schema": "http://schema.org#",
+ "PropertyValue": "schema:PropertyValue",
+ "value": "schema:value",
+ "Hashtag": "as:Hashtag",
+ "Emoji": "toot:Emoji",
+ "IdentityProof": "toot:IdentityProof",
+ "focalPoint": {
+ "@container": "@list",
+ "@id": "toot:focalPoint"
+ }
+ }
+ ],
+ "id": "https://mastodon.social/users/lambadalambda",
+ "type": "Person",
+ "following": "https://mastodon.social/users/lambadalambda/following",
+ "followers": "https://mastodon.social/users/lambadalambda/followers",
+ "inbox": "https://mastodon.social/users/lambadalambda/inbox",
+ "outbox": "https://mastodon.social/users/lambadalambda/outbox",
+ "featured": "https://mastodon.social/users/lambadalambda/collections/featured",
+ "preferredUsername": "lambadalambda",
+ "name": "Critical Value",
+ "summary": "\u003cp\u003e\u003c/p\u003e",
+ "url": "https://mastodon.social/@lambadalambda",
+ "manuallyApprovesFollowers": false,
+ "publicKey": {
+ "id": "https://mastodon.social/users/lambadalambda#main-key",
+ "owner": "https://mastodon.social/users/lambadalambda",
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0P/Tq4gb4G/QVuMGbJo\nC/AfMNcv+m7NfrlOwkVzcU47jgESuYI4UtJayissCdBycHUnfVUd9qol+eznSODz\nCJhfJloqEIC+aSnuEPGA0POtWad6DU0E6/Ho5zQn5WAWUwbRQqowbrsm/GHo2+3v\neR5jGenwA6sYhINg/c3QQbksyV0uJ20Umyx88w8+TJuv53twOfmyDWuYNoQ3y5cc\nHKOZcLHxYOhvwg3PFaGfFHMFiNmF40dTXt9K96r7sbzc44iLD+VphbMPJEjkMuf8\nPGEFOBzy8pm3wJZw2v32RNW2VESwMYyqDzwHXGSq1a73cS7hEnc79gXlELsK04L9\nQQIDAQAB\n-----END PUBLIC KEY-----\n"
+ },
+ "tag": [],
+ "attachment": [],
+ "endpoints": {
+ "sharedInbox": "https://mastodon.social/inbox"
+ },
+ "icon": {
+ "type": "Image",
+ "mediaType": "image/gif",
+ "url": "https://files.mastodon.social/accounts/avatars/000/000/264/original/1429214160519.gif"
+ },
+ "image": {
+ "type": "Image",
+ "mediaType": "image/gif",
+ "url": "https://files.mastodon.social/accounts/headers/000/000/264/original/28b26104f83747d2.gif"
+ }
+}
diff --git a/test/notification_test.exs b/test/notification_test.exs
index 12b4292aa..c3db77b6c 100644
--- a/test/notification_test.exs
+++ b/test/notification_test.exs
@@ -29,6 +29,18 @@ defmodule Pleroma.NotificationTest do
assert notification.activity_id == activity.id
assert other_notification.activity_id == activity.id
end
+
+ test "it creates a notification for subscribed users" do
+ user = insert(:user)
+ subscriber = insert(:user)
+
+ User.subscribe(subscriber, user)
+
+ {:ok, status} = TwitterAPI.create_status(user, %{"status" => "Akariiiin"})
+ {:ok, [notification]} = Notification.create_notifications(status)
+
+ assert notification.user_id == subscriber.id
+ end
end
describe "create_notification" do
@@ -41,6 +53,75 @@ defmodule Pleroma.NotificationTest do
assert nil == Notification.create_notification(activity, user)
end
+ test "it doesn't create a notificatin for the user if the user mutes the activity author" do
+ muter = insert(:user)
+ muted = insert(:user)
+ {:ok, _} = User.mute(muter, muted)
+ muter = Repo.get(User, muter.id)
+ {:ok, activity} = CommonAPI.post(muted, %{"status" => "Hi @#{muter.nickname}"})
+
+ assert nil == Notification.create_notification(activity, muter)
+ end
+
+ test "it doesn't create a notification for an activity from a muted thread" do
+ muter = insert(:user)
+ other_user = insert(:user)
+ {:ok, activity} = CommonAPI.post(muter, %{"status" => "hey"})
+ CommonAPI.add_mute(muter, activity)
+
+ {:ok, activity} =
+ CommonAPI.post(other_user, %{
+ "status" => "Hi @#{muter.nickname}",
+ "in_reply_to_status_id" => activity.id
+ })
+
+ assert nil == Notification.create_notification(activity, muter)
+ end
+
+ test "it disables notifications from people on remote instances" do
+ user = insert(:user, info: %{notification_settings: %{"remote" => false}})
+ other_user = insert(:user)
+
+ create_activity = %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "type" => "Create",
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "actor" => other_user.ap_id,
+ "object" => %{
+ "type" => "Note",
+ "content" => "Hi @#{user.nickname}",
+ "attributedTo" => other_user.ap_id
+ }
+ }
+
+ {:ok, %{local: false} = activity} = Transmogrifier.handle_incoming(create_activity)
+ assert nil == Notification.create_notification(activity, user)
+ end
+
+ test "it disables notifications from people on the local instance" do
+ user = insert(:user, info: %{notification_settings: %{"local" => false}})
+ other_user = insert(:user)
+ {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
+ assert nil == Notification.create_notification(activity, user)
+ end
+
+ test "it disables notifications from followers" do
+ follower = insert(:user)
+ followed = insert(:user, info: %{notification_settings: %{"followers" => false}})
+ User.follow(follower, followed)
+ {:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"})
+ assert nil == Notification.create_notification(activity, followed)
+ end
+
+ test "it disables notifications from people the user follows" do
+ follower = insert(:user, info: %{notification_settings: %{"follows" => false}})
+ followed = insert(:user)
+ User.follow(follower, followed)
+ follower = Repo.get(User, follower.id)
+ {:ok, activity} = CommonAPI.post(followed, %{"status" => "hey @#{follower.nickname}"})
+ assert nil == Notification.create_notification(activity, follower)
+ end
+
test "it doesn't create a notification for user if he is the activity author" do
activity = insert(:note_activity)
author = User.get_by_ap_id(activity.data["actor"])
@@ -84,6 +165,28 @@ defmodule Pleroma.NotificationTest do
{:ok, dupe} = TwitterAPI.repeat(user, status.id)
assert nil == Notification.create_notification(dupe, retweeted_user)
end
+
+ test "it doesn't create duplicate notifications for follow+subscribed users" do
+ user = insert(:user)
+ subscriber = insert(:user)
+
+ {:ok, _, _, _} = TwitterAPI.follow(subscriber, %{"user_id" => user.id})
+ User.subscribe(subscriber, user)
+ {:ok, status} = TwitterAPI.create_status(user, %{"status" => "Akariiiin"})
+ {:ok, [_notif]} = Notification.create_notifications(status)
+ end
+
+ test "it doesn't create subscription notifications if the recipient cannot see the status" do
+ user = insert(:user)
+ subscriber = insert(:user)
+
+ User.subscribe(subscriber, user)
+
+ {:ok, status} =
+ TwitterAPI.create_status(user, %{"status" => "inwisible", "visibility" => "direct"})
+
+ assert {:ok, []} == Notification.create_notifications(status)
+ end
end
describe "get notification" do
diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex
index d3b547d91..5b355bfe6 100644
--- a/test/support/http_request_mock.ex
+++ b/test/support/http_request_mock.ex
@@ -716,6 +716,10 @@ defmodule HttpRequestMock do
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/lambadalambda.atom")}}
end
+ def get("https://mastodon.social/users/lambadalambda", _, _, _) do
+ {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/lambadalambda.json")}}
+ end
+
def get("https://social.heldscal.la/user/23211", _, _, Accept: "application/activity+json") do
{:ok, Tesla.Mock.json(%{"id" => "https://social.heldscal.la/user/23211"}, status: 200)}
end
diff --git a/test/tasks/user_test.exs b/test/tasks/user_test.exs
index 1030bd555..242265da5 100644
--- a/test/tasks/user_test.exs
+++ b/test/tasks/user_test.exs
@@ -245,7 +245,87 @@ defmodule Mix.Tasks.Pleroma.UserTest do
end) =~ "http"
assert_received {:mix_shell, :info, [message]}
- assert message =~ "Generated"
+ assert message =~ "Generated user invite token one time"
+ end
+
+ test "token is generated with expires_at" do
+ assert capture_io(fn ->
+ Mix.Tasks.Pleroma.User.run([
+ "invite",
+ "--expires-at",
+ Date.to_string(Date.utc_today())
+ ])
+ end)
+
+ assert_received {:mix_shell, :info, [message]}
+ assert message =~ "Generated user invite token date limited"
+ end
+
+ test "token is generated with max use" do
+ assert capture_io(fn ->
+ Mix.Tasks.Pleroma.User.run([
+ "invite",
+ "--max-use",
+ "5"
+ ])
+ end)
+
+ assert_received {:mix_shell, :info, [message]}
+ assert message =~ "Generated user invite token reusable"
+ end
+
+ test "token is generated with max use and expires date" do
+ assert capture_io(fn ->
+ Mix.Tasks.Pleroma.User.run([
+ "invite",
+ "--max-use",
+ "5",
+ "--expires-at",
+ Date.to_string(Date.utc_today())
+ ])
+ end)
+
+ assert_received {:mix_shell, :info, [message]}
+ assert message =~ "Generated user invite token reusable date limited"
+ end
+ end
+
+ describe "running invites" do
+ test "invites are listed" do
+ {:ok, invite} = Pleroma.UserInviteToken.create_invite()
+
+ {:ok, invite2} =
+ Pleroma.UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 15})
+
+ # assert capture_io(fn ->
+ Mix.Tasks.Pleroma.User.run([
+ "invites"
+ ])
+
+ # end)
+
+ assert_received {:mix_shell, :info, [message]}
+ assert_received {:mix_shell, :info, [message2]}
+ assert_received {:mix_shell, :info, [message3]}
+ assert message =~ "Invites list:"
+ assert message2 =~ invite.invite_type
+ assert message3 =~ invite2.invite_type
+ end
+ end
+
+ describe "running revoke_invite" do
+ test "invite is revoked" do
+ {:ok, invite} = Pleroma.UserInviteToken.create_invite(%{expires_at: Date.utc_today()})
+
+ assert capture_io(fn ->
+ Mix.Tasks.Pleroma.User.run([
+ "revoke_invite",
+ invite.token
+ ])
+ end)
+
+ assert_received {:mix_shell, :info, [message]}
+ assert message =~ "Invite for token #{invite.token} was revoked."
end
end
diff --git a/test/user_invite_token_test.exs b/test/user_invite_token_test.exs
new file mode 100644
index 000000000..276788254
--- /dev/null
+++ b/test/user_invite_token_test.exs
@@ -0,0 +1,96 @@
+defmodule Pleroma.UserInviteTokenTest do
+ use ExUnit.Case, async: true
+ use Pleroma.DataCase
+ alias Pleroma.UserInviteToken
+
+ describe "valid_invite?/1 one time invites" do
+ setup do
+ invite = %UserInviteToken{invite_type: "one_time"}
+
+ {:ok, invite: invite}
+ end
+
+ test "not used returns true", %{invite: invite} do
+ invite = %{invite | used: false}
+ assert UserInviteToken.valid_invite?(invite)
+ end
+
+ test "used returns false", %{invite: invite} do
+ invite = %{invite | used: true}
+ refute UserInviteToken.valid_invite?(invite)
+ end
+ end
+
+ describe "valid_invite?/1 reusable invites" do
+ setup do
+ invite = %UserInviteToken{
+ invite_type: "reusable",
+ max_use: 5
+ }
+
+ {:ok, invite: invite}
+ end
+
+ test "with less uses then max use returns true", %{invite: invite} do
+ invite = %{invite | uses: 4}
+ assert UserInviteToken.valid_invite?(invite)
+ end
+
+ test "with equal or more uses then max use returns false", %{invite: invite} do
+ invite = %{invite | uses: 5}
+
+ refute UserInviteToken.valid_invite?(invite)
+
+ invite = %{invite | uses: 6}
+
+ refute UserInviteToken.valid_invite?(invite)
+ end
+ end
+
+ describe "valid_token?/1 date limited invites" do
+ setup do
+ invite = %UserInviteToken{invite_type: "date_limited"}
+ {:ok, invite: invite}
+ end
+
+ test "expires today returns true", %{invite: invite} do
+ invite = %{invite | expires_at: Date.utc_today()}
+ assert UserInviteToken.valid_invite?(invite)
+ end
+
+ test "expires yesterday returns false", %{invite: invite} do
+ invite = %{invite | expires_at: Date.add(Date.utc_today(), -1)}
+ invite = Repo.insert!(invite)
+ refute UserInviteToken.valid_invite?(invite)
+ end
+ end
+
+ describe "valid_token?/1 reusable date limited invites" do
+ setup do
+ invite = %UserInviteToken{invite_type: "reusable_date_limited", max_use: 5}
+ {:ok, invite: invite}
+ end
+
+ test "not overdue date and less uses returns true", %{invite: invite} do
+ invite = %{invite | expires_at: Date.utc_today(), uses: 4}
+ assert UserInviteToken.valid_invite?(invite)
+ end
+
+ test "overdue date and less uses returns false", %{invite: invite} do
+ invite = %{invite | expires_at: Date.add(Date.utc_today(), -1)}
+ invite = Repo.insert!(invite)
+ refute UserInviteToken.valid_invite?(invite)
+ end
+
+ test "not overdue date with more uses returns false", %{invite: invite} do
+ invite = %{invite | expires_at: Date.utc_today(), uses: 5}
+ refute UserInviteToken.valid_invite?(invite)
+ end
+
+ test "overdue date with more uses returns false", %{invite: invite} do
+ invite = %{invite | expires_at: Date.add(Date.utc_today(), -1), uses: 5}
+ invite = Repo.insert!(invite)
+ refute UserInviteToken.valid_invite?(invite)
+ end
+ end
+end
diff --git a/test/user_test.exs b/test/user_test.exs
index 4f98af683..d2167a970 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -146,6 +146,15 @@ defmodule Pleroma.UserTest do
{:error, _} = User.follow(blockee, blocker)
end
+ test "can't subscribe to a user who blocked us" do
+ blocker = insert(:user)
+ blocked = insert(:user)
+
+ {:ok, blocker} = User.block(blocker, blocked)
+
+ {:error, _} = User.subscribe(blocked, blocker)
+ end
+
test "local users do not automatically follow local locked accounts" do
follower = insert(:user, info: %{locked: true})
followed = insert(:user, info: %{locked: true})
@@ -729,6 +738,22 @@ defmodule Pleroma.UserTest do
refute User.following?(blocker, blocked)
refute User.following?(blocked, blocker)
end
+
+ test "blocks tear down blocked->blocker subscription relationships" do
+ blocker = insert(:user)
+ blocked = insert(:user)
+
+ {:ok, blocker} = User.subscribe(blocked, blocker)
+
+ assert User.subscribed_to?(blocked, blocker)
+ refute User.subscribed_to?(blocker, blocked)
+
+ {:ok, blocker} = User.block(blocker, blocked)
+
+ assert User.blocks?(blocker, blocked)
+ refute User.subscribed_to?(blocker, blocked)
+ refute User.subscribed_to?(blocked, blocker)
+ end
end
describe "domain blocking" do
diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs
index 7b1f6d53a..ca7794d70 100644
--- a/test/web/admin_api/admin_api_controller_test.exs
+++ b/test/web/admin_api/admin_api_controller_test.exs
@@ -6,6 +6,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
use Pleroma.Web.ConnCase
alias Pleroma.User
+ alias Pleroma.UserInviteToken
import Pleroma.Factory
describe "/api/pleroma/admin/user" do
@@ -648,4 +649,136 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"tags" => []
}
end
+
+ describe "GET /api/pleroma/admin/invite_token" do
+ test "without options" do
+ admin = insert(:user, info: %{is_admin: true})
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/invite_token")
+
+ token = json_response(conn, 200)
+ invite = UserInviteToken.find_by_token!(token)
+ refute invite.used
+ refute invite.expires_at
+ refute invite.max_use
+ assert invite.invite_type == "one_time"
+ end
+
+ test "with expires_at" do
+ admin = insert(:user, info: %{is_admin: true})
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/invite_token", %{
+ "invite" => %{"expires_at" => Date.to_string(Date.utc_today())}
+ })
+
+ token = json_response(conn, 200)
+ invite = UserInviteToken.find_by_token!(token)
+
+ refute invite.used
+ assert invite.expires_at == Date.utc_today()
+ refute invite.max_use
+ assert invite.invite_type == "date_limited"
+ end
+
+ test "with max_use" do
+ admin = insert(:user, info: %{is_admin: true})
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/invite_token", %{
+ "invite" => %{"max_use" => 150}
+ })
+
+ token = json_response(conn, 200)
+ invite = UserInviteToken.find_by_token!(token)
+ refute invite.used
+ refute invite.expires_at
+ assert invite.max_use == 150
+ assert invite.invite_type == "reusable"
+ end
+
+ test "with max use and expires_at" do
+ admin = insert(:user, info: %{is_admin: true})
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/invite_token", %{
+ "invite" => %{"max_use" => 150, "expires_at" => Date.to_string(Date.utc_today())}
+ })
+
+ token = json_response(conn, 200)
+ invite = UserInviteToken.find_by_token!(token)
+ refute invite.used
+ assert invite.expires_at == Date.utc_today()
+ assert invite.max_use == 150
+ assert invite.invite_type == "reusable_date_limited"
+ end
+ end
+
+ describe "GET /api/pleroma/admin/invites" do
+ test "no invites" do
+ admin = insert(:user, info: %{is_admin: true})
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/invites")
+
+ assert json_response(conn, 200) == %{"invites" => []}
+ end
+
+ test "with invite" do
+ admin = insert(:user, info: %{is_admin: true})
+ {:ok, invite} = UserInviteToken.create_invite()
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/invites")
+
+ assert json_response(conn, 200) == %{
+ "invites" => [
+ %{
+ "expires_at" => nil,
+ "id" => invite.id,
+ "invite_type" => "one_time",
+ "max_use" => nil,
+ "token" => invite.token,
+ "used" => false,
+ "uses" => 0
+ }
+ ]
+ }
+ end
+ end
+
+ describe "POST /api/pleroma/admin/revoke_invite" do
+ test "with token" do
+ admin = insert(:user, info: %{is_admin: true})
+ {:ok, invite} = UserInviteToken.create_invite()
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> post("/api/pleroma/admin/revoke_invite", %{"token" => invite.token})
+
+ assert json_response(conn, 200) == %{
+ "expires_at" => nil,
+ "id" => invite.id,
+ "invite_type" => "one_time",
+ "max_use" => nil,
+ "token" => invite.token,
+ "used" => true,
+ "uses" => 0
+ }
+ end
+ end
end
diff --git a/test/web/mastodon_api/account_view_test.exs b/test/web/mastodon_api/account_view_test.exs
index 6dc60afe9..d7487bed9 100644
--- a/test/web/mastodon_api/account_view_test.exs
+++ b/test/web/mastodon_api/account_view_test.exs
@@ -71,6 +71,20 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
assert expected == AccountView.render("account.json", %{user: user})
end
+ test "Represent the user account for the account owner" do
+ user = insert(:user)
+
+ notification_settings = %{
+ "remote" => true,
+ "local" => true,
+ "followers" => true,
+ "follows" => true
+ }
+
+ assert %{pleroma: %{notification_settings: ^notification_settings}} =
+ AccountView.render("account.json", %{user: user, for: user})
+ end
+
test "Represent a Service(bot) account" do
user =
insert(:user, %{
@@ -142,6 +156,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
blocking: true,
muting: false,
muting_notifications: false,
+ subscribing: false,
requested: false,
domain_blocking: false,
showing_reblogs: true,
@@ -198,6 +213,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
following: false,
followed_by: false,
blocking: true,
+ subscribing: false,
muting: false,
muting_notifications: false,
requested: false,
diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs
index 24e258d66..614b950ba 100644
--- a/test/web/mastodon_api/mastodon_api_controller_test.exs
+++ b/test/web/mastodon_api/mastodon_api_controller_test.exs
@@ -1556,6 +1556,25 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
end
+ test "subscribing / unsubscribing to a user", %{conn: conn} do
+ user = insert(:user)
+ subscription_target = insert(:user)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
+
+ assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
+
+ conn =
+ build_conn()
+ |> assign(:user, user)
+ |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
+
+ assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
+ end
+
test "getting a list of mutes", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)
diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs
index 24e46408c..b61e2a24c 100644
--- a/test/web/twitter_api/twitter_api_test.exs
+++ b/test/web/twitter_api/twitter_api_test.exs
@@ -16,6 +16,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
import Pleroma.Factory
+ setup_all do
+ Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
test "create a status" do
user = insert(:user)
mentioned_user = insert(:user, %{nickname: "shp", ap_id: "shp"})
@@ -299,7 +304,6 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
UserView.render("show.json", %{user: fetched_user})
end
- @moduletag skip: "needs 'account_activation_required: true' in config"
test "it sends confirmation email if :account_activation_required is specified in instance config" do
setting = Pleroma.Config.get([:instance, :account_activation_required])
@@ -362,68 +366,313 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
assert user2.bio == expected_text
end
- @moduletag skip: "needs 'registrations_open: false' in config"
- test "it registers a new user via invite token and returns the user." do
- {:ok, token} = UserInviteToken.create_token()
+ describe "register with one time token" do
+ setup do
+ setting = Pleroma.Config.get([:instance, :registrations_open])
- data = %{
- "nickname" => "vinny",
- "email" => "pasta@pizza.vs",
- "fullname" => "Vinny Vinesauce",
- "bio" => "streamer",
- "password" => "hiptofbees",
- "confirm" => "hiptofbees",
- "token" => token.token
- }
+ if setting do
+ Pleroma.Config.put([:instance, :registrations_open], false)
+ on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
+ end
- {:ok, user} = TwitterAPI.register_user(data)
+ :ok
+ end
- fetched_user = User.get_by_nickname("vinny")
- token = Repo.get_by(UserInviteToken, token: token.token)
+ test "returns user on success" do
+ {:ok, invite} = UserInviteToken.create_invite()
- assert token.used == true
+ data = %{
+ "nickname" => "vinny",
+ "email" => "pasta@pizza.vs",
+ "fullname" => "Vinny Vinesauce",
+ "bio" => "streamer",
+ "password" => "hiptofbees",
+ "confirm" => "hiptofbees",
+ "token" => invite.token
+ }
- assert UserView.render("show.json", %{user: user}) ==
- UserView.render("show.json", %{user: fetched_user})
+ {:ok, user} = TwitterAPI.register_user(data)
+
+ fetched_user = User.get_by_nickname("vinny")
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+
+ assert invite.used == true
+
+ assert UserView.render("show.json", %{user: user}) ==
+ UserView.render("show.json", %{user: fetched_user})
+ end
+
+ test "returns error on invalid token" do
+ data = %{
+ "nickname" => "GrimReaper",
+ "email" => "death@reapers.afterlife",
+ "fullname" => "Reaper Grim",
+ "bio" => "Your time has come",
+ "password" => "scythe",
+ "confirm" => "scythe",
+ "token" => "DudeLetMeInImAFairy"
+ }
+
+ {:error, msg} = TwitterAPI.register_user(data)
+
+ assert msg == "Invalid token"
+ refute User.get_by_nickname("GrimReaper")
+ end
+
+ test "returns error on expired token" do
+ {:ok, invite} = UserInviteToken.create_invite()
+ UserInviteToken.update_invite!(invite, used: true)
+
+ data = %{
+ "nickname" => "GrimReaper",
+ "email" => "death@reapers.afterlife",
+ "fullname" => "Reaper Grim",
+ "bio" => "Your time has come",
+ "password" => "scythe",
+ "confirm" => "scythe",
+ "token" => invite.token
+ }
+
+ {:error, msg} = TwitterAPI.register_user(data)
+
+ assert msg == "Expired token"
+ refute User.get_by_nickname("GrimReaper")
+ end
end
- @moduletag skip: "needs 'registrations_open: false' in config"
- test "it returns an error if invalid token submitted" do
- data = %{
- "nickname" => "GrimReaper",
- "email" => "death@reapers.afterlife",
- "fullname" => "Reaper Grim",
- "bio" => "Your time has come",
- "password" => "scythe",
- "confirm" => "scythe",
- "token" => "DudeLetMeInImAFairy"
- }
+ describe "registers with date limited token" do
+ setup do
+ setting = Pleroma.Config.get([:instance, :registrations_open])
+
+ if setting do
+ Pleroma.Config.put([:instance, :registrations_open], false)
+ on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
+ end
+
+ data = %{
+ "nickname" => "vinny",
+ "email" => "pasta@pizza.vs",
+ "fullname" => "Vinny Vinesauce",
+ "bio" => "streamer",
+ "password" => "hiptofbees",
+ "confirm" => "hiptofbees"
+ }
+
+ check_fn = fn invite ->
+ data = Map.put(data, "token", invite.token)
+ {:ok, user} = TwitterAPI.register_user(data)
+ fetched_user = User.get_by_nickname("vinny")
+
+ assert UserView.render("show.json", %{user: user}) ==
+ UserView.render("show.json", %{user: fetched_user})
+ end
+
+ {:ok, data: data, check_fn: check_fn}
+ end
+
+ test "returns user on success", %{check_fn: check_fn} do
+ {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today()})
+
+ check_fn.(invite)
+
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+
+ refute invite.used
+ end
- {:error, msg} = TwitterAPI.register_user(data)
+ test "returns user on token which expired tomorrow", %{check_fn: check_fn} do
+ {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), 1)})
- assert msg == "Invalid token"
- refute User.get_by_nickname("GrimReaper")
+ check_fn.(invite)
+
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+
+ refute invite.used
+ end
+
+ test "returns an error on overdue date", %{data: data} do
+ {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1)})
+
+ data = Map.put(data, "token", invite.token)
+
+ {:error, msg} = TwitterAPI.register_user(data)
+
+ assert msg == "Expired token"
+ refute User.get_by_nickname("vinny")
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+
+ refute invite.used
+ end
end
- @moduletag skip: "needs 'registrations_open: false' in config"
- test "it returns an error if expired token submitted" do
- {:ok, token} = UserInviteToken.create_token()
- UserInviteToken.mark_as_used(token.token)
+ describe "registers with reusable token" do
+ setup do
+ setting = Pleroma.Config.get([:instance, :registrations_open])
- data = %{
- "nickname" => "GrimReaper",
- "email" => "death@reapers.afterlife",
- "fullname" => "Reaper Grim",
- "bio" => "Your time has come",
- "password" => "scythe",
- "confirm" => "scythe",
- "token" => token.token
- }
+ if setting do
+ Pleroma.Config.put([:instance, :registrations_open], false)
+ on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
+ end
+
+ :ok
+ end
+
+ test "returns user on success, after him registration fails" do
+ {:ok, invite} = UserInviteToken.create_invite(%{max_use: 100})
+
+ UserInviteToken.update_invite!(invite, uses: 99)
+
+ data = %{
+ "nickname" => "vinny",
+ "email" => "pasta@pizza.vs",
+ "fullname" => "Vinny Vinesauce",
+ "bio" => "streamer",
+ "password" => "hiptofbees",
+ "confirm" => "hiptofbees",
+ "token" => invite.token
+ }
+
+ {:ok, user} = TwitterAPI.register_user(data)
+ fetched_user = User.get_by_nickname("vinny")
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+
+ assert invite.used == true
+
+ assert UserView.render("show.json", %{user: user}) ==
+ UserView.render("show.json", %{user: fetched_user})
+
+ data = %{
+ "nickname" => "GrimReaper",
+ "email" => "death@reapers.afterlife",
+ "fullname" => "Reaper Grim",
+ "bio" => "Your time has come",
+ "password" => "scythe",
+ "confirm" => "scythe",
+ "token" => invite.token
+ }
+
+ {:error, msg} = TwitterAPI.register_user(data)
+
+ assert msg == "Expired token"
+ refute User.get_by_nickname("GrimReaper")
+ end
+ end
+
+ describe "registers with reusable date limited token" do
+ setup do
+ setting = Pleroma.Config.get([:instance, :registrations_open])
+
+ if setting do
+ Pleroma.Config.put([:instance, :registrations_open], false)
+ on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
+ end
+
+ :ok
+ end
+
+ test "returns user on success" do
+ {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100})
+
+ data = %{
+ "nickname" => "vinny",
+ "email" => "pasta@pizza.vs",
+ "fullname" => "Vinny Vinesauce",
+ "bio" => "streamer",
+ "password" => "hiptofbees",
+ "confirm" => "hiptofbees",
+ "token" => invite.token
+ }
+
+ {:ok, user} = TwitterAPI.register_user(data)
+ fetched_user = User.get_by_nickname("vinny")
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+
+ refute invite.used
+
+ assert UserView.render("show.json", %{user: user}) ==
+ UserView.render("show.json", %{user: fetched_user})
+ end
+
+ test "error after max uses" do
+ {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100})
+
+ UserInviteToken.update_invite!(invite, uses: 99)
+
+ data = %{
+ "nickname" => "vinny",
+ "email" => "pasta@pizza.vs",
+ "fullname" => "Vinny Vinesauce",
+ "bio" => "streamer",
+ "password" => "hiptofbees",
+ "confirm" => "hiptofbees",
+ "token" => invite.token
+ }
+
+ {:ok, user} = TwitterAPI.register_user(data)
+ fetched_user = User.get_by_nickname("vinny")
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+ assert invite.used == true
+
+ assert UserView.render("show.json", %{user: user}) ==
+ UserView.render("show.json", %{user: fetched_user})
+
+ data = %{
+ "nickname" => "GrimReaper",
+ "email" => "death@reapers.afterlife",
+ "fullname" => "Reaper Grim",
+ "bio" => "Your time has come",
+ "password" => "scythe",
+ "confirm" => "scythe",
+ "token" => invite.token
+ }
+
+ {:error, msg} = TwitterAPI.register_user(data)
+
+ assert msg == "Expired token"
+ refute User.get_by_nickname("GrimReaper")
+ end
- {:error, msg} = TwitterAPI.register_user(data)
+ test "returns error on overdue date" do
+ {:ok, invite} =
+ UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1), max_use: 100})
- assert msg == "Expired token"
- refute User.get_by_nickname("GrimReaper")
+ data = %{
+ "nickname" => "GrimReaper",
+ "email" => "death@reapers.afterlife",
+ "fullname" => "Reaper Grim",
+ "bio" => "Your time has come",
+ "password" => "scythe",
+ "confirm" => "scythe",
+ "token" => invite.token
+ }
+
+ {:error, msg} = TwitterAPI.register_user(data)
+
+ assert msg == "Expired token"
+ refute User.get_by_nickname("GrimReaper")
+ end
+
+ test "returns error on with overdue date and after max" do
+ {:ok, invite} =
+ UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1), max_use: 100})
+
+ UserInviteToken.update_invite!(invite, uses: 100)
+
+ data = %{
+ "nickname" => "GrimReaper",
+ "email" => "death@reapers.afterlife",
+ "fullname" => "Reaper Grim",
+ "bio" => "Your time has come",
+ "password" => "scythe",
+ "confirm" => "scythe",
+ "token" => invite.token
+ }
+
+ {:error, msg} = TwitterAPI.register_user(data)
+
+ assert msg == "Expired token"
+ refute User.get_by_nickname("GrimReaper")
+ end
end
test "it returns the error on registration problems" do
diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs
index 410f20f87..a4b3d651a 100644
--- a/test/web/twitter_api/util_controller_test.exs
+++ b/test/web/twitter_api/util_controller_test.exs
@@ -3,6 +3,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
alias Pleroma.Notification
alias Pleroma.Repo
+ alias Pleroma.User
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
@@ -79,6 +80,26 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
end
end
+ describe "PUT /api/pleroma/notification_settings" do
+ test "it updates notification settings", %{conn: conn} do
+ user = insert(:user)
+
+ conn
+ |> assign(:user, user)
+ |> put("/api/pleroma/notification_settings", %{
+ "remote" => false,
+ "followers" => false,
+ "bar" => 1
+ })
+ |> json_response(:ok)
+
+ user = Repo.get(User, user.id)
+
+ assert %{"remote" => false, "local" => true, "followers" => false, "follows" => true} ==
+ user.info.notification_settings
+ end
+ end
+
describe "GET /api/statusnet/config.json" do
test "returns the state of safe_dm_mentions flag", %{conn: conn} do
option = Pleroma.Config.get([:instance, :safe_dm_mentions])
@@ -172,22 +193,19 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
describe "/api/pleroma/emoji" do
test "returns json with custom emoji with tags", %{conn: conn} do
- [emoji | _body] =
+ emoji =
conn
|> get("/api/pleroma/emoji")
|> json_response(200)
- [key] = Map.keys(emoji)
-
- %{
- ^key => %{
- "image_url" => url,
- "tags" => tags
- }
- } = emoji
-
- assert is_binary(url)
- assert is_list(tags)
+ assert Enum.all?(emoji, fn
+ {_key,
+ %{
+ "image_url" => url,
+ "tags" => tags
+ }} ->
+ is_binary(url) and is_list(tags)
+ end)
end
end