diff --git a/lib/graphql/middleware/current_actor_provider.ex b/lib/graphql/middleware/current_actor_provider.ex new file mode 100644 index 000000000..d34eb2a9e --- /dev/null +++ b/lib/graphql/middleware/current_actor_provider.ex @@ -0,0 +1,40 @@ +defmodule Mobilizon.GraphQL.Middleware.CurrentActorProvider do + @moduledoc """ + Absinthe Error Handler + """ + alias Mobilizon.Actors.Actor + alias Mobilizon.Users + alias Mobilizon.Users.User + + @behaviour Absinthe.Middleware + + @impl Absinthe.Middleware + @spec call(Absinthe.Resolution.t(), any) :: Absinthe.Resolution.t() + def call( + %Absinthe.Resolution{context: %{current_user: %User{id: user_id} = user} = context} = + resolution, + _config + ) do + case Cachex.fetch(:default_actors, to_string(user_id), fn -> default(user) end) do + {status, %Actor{} = current_actor} when status in [:ok, :commit] -> + context = Map.put(context, :current_actor, current_actor) + %Absinthe.Resolution{resolution | context: context} + + {_, nil} -> + resolution + end + end + + def call(%Absinthe.Resolution{} = resolution, _config), do: resolution + + @spec default(User.t()) :: {:commit, Actor.t()} | {:ignore, nil} + defp default(%User{} = user) do + case Users.get_actor_for_user(user) do + %Actor{} = actor -> + {:commit, actor} + + nil -> + {:ignore, nil} + end + end +end diff --git a/lib/graphql/resolvers/activity.ex b/lib/graphql/resolvers/activity.ex index ae683341a..6f7a4df26 100644 --- a/lib/graphql/resolvers/activity.ex +++ b/lib/graphql/resolvers/activity.ex @@ -4,7 +4,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Activity do """ import Mobilizon.Users.Guards - alias Mobilizon.{Activities, Actors, Users} + alias Mobilizon.{Activities, Actors} alias Mobilizon.Actors.Actor alias Mobilizon.Service.Activity.Utils alias Mobilizon.Storage.Page @@ -13,29 +13,23 @@ defmodule Mobilizon.GraphQL.Resolvers.Activity do require Logger def group_activity(%Actor{type: :Group, id: group_id}, %{page: page, limit: limit} = args, %{ - context: %{current_user: %User{role: role} = user} + context: %{current_user: %User{role: role}, current_actor: %Actor{id: actor_id}} }) do - case Users.get_actor_for_user(user) do - %Actor{id: actor_id} = _actor -> - if Actors.is_member?(actor_id, group_id) or is_moderator(role) do - %Page{total: total, elements: elements} = - Activities.list_group_activities_for_member( - group_id, - actor_id, - [type: Map.get(args, :type), author: Map.get(args, :author)], - page, - limit - ) + if Actors.is_member?(actor_id, group_id) or is_moderator(role) do + %Page{total: total, elements: elements} = + Activities.list_group_activities_for_member( + group_id, + actor_id, + [type: Map.get(args, :type), author: Map.get(args, :author)], + page, + limit + ) - elements = Enum.map(elements, &Utils.transform_activity/1) + elements = Enum.map(elements, &Utils.transform_activity/1) - {:ok, %Page{total: total, elements: elements}} - else - {:error, :unauthorized} - end - - nil -> - {:error, :user_actor_not_found} + {:ok, %Page{total: total, elements: elements}} + else + {:error, :unauthorized} end end diff --git a/lib/graphql/resolvers/actor.ex b/lib/graphql/resolvers/actor.ex index c1279d9b9..7f064b5eb 100644 --- a/lib/graphql/resolvers/actor.ex +++ b/lib/graphql/resolvers/actor.ex @@ -4,7 +4,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Actor do """ import Mobilizon.Users.Guards - alias Mobilizon.{Actors, Admin, Users} + alias Mobilizon.{Actors, Admin} alias Mobilizon.Actors.Actor alias Mobilizon.Federation.ActivityPub alias Mobilizon.Service.Workers.Background @@ -32,39 +32,35 @@ defmodule Mobilizon.GraphQL.Resolvers.Actor do end def suspend_profile(_parent, %{id: id}, %{ - context: %{current_user: %User{role: role} = user} + context: %{ + current_user: %User{role: role}, + current_actor: %Actor{} = moderator_actor + } }) when is_moderator(role) do - with {:moderator_actor, %Actor{} = moderator_actor} <- - {:moderator_actor, Users.get_actor_for_user(user)}, - %Actor{suspended: false} = actor <- Actors.get_actor_with_preload(id) do - case actor do - # Suspend a group on this instance - %Actor{type: :Group, domain: nil} -> - Logger.debug("We're suspending a group on this very instance") - ActivityPub.delete(actor, moderator_actor, true, %{suspension: true}) - Admin.log_action(moderator_actor, "suspend", actor) - {:ok, actor} + case Actors.get_actor_with_preload(id) do + %Actor{suspended: false} = actor -> + case actor do + # Suspend a group on this instance + %Actor{type: :Group, domain: nil} -> + Logger.debug("We're suspending a group on this very instance") + ActivityPub.delete(actor, moderator_actor, true, %{suspension: true}) + Admin.log_action(moderator_actor, "suspend", actor) + {:ok, actor} - # Delete a remote actor - %Actor{domain: domain} when not is_nil(domain) -> - Logger.debug("We're just deleting a remote instance") - Actors.delete_actor(actor, suspension: true) - Admin.log_action(moderator_actor, "suspend", actor) - {:ok, actor} + # Delete a remote actor + %Actor{domain: domain} when not is_nil(domain) -> + Logger.debug("We're just deleting a remote instance") + Actors.delete_actor(actor, suspension: true) + Admin.log_action(moderator_actor, "suspend", actor) + {:ok, actor} - %Actor{domain: nil} -> - {:error, dgettext("errors", "No remote profile found with this ID")} - end - else - {:moderator_actor, nil} -> - {:error, dgettext("errors", "No profile found for the moderator user")} + %Actor{domain: nil} -> + {:error, dgettext("errors", "No remote profile found with this ID")} + end %Actor{suspended: true} -> {:error, dgettext("errors", "Profile already suspended")} - - {:error, _} -> - {:error, dgettext("errors", "Error while performing background task")} end end @@ -73,12 +69,13 @@ defmodule Mobilizon.GraphQL.Resolvers.Actor do end def unsuspend_profile(_parent, %{id: id}, %{ - context: %{current_user: %User{role: role} = user} + context: %{ + current_user: %User{role: role}, + current_actor: %Actor{} = moderator_actor + } }) when is_moderator(role) do - with {:moderator_actor, %Actor{} = moderator_actor} <- - {:moderator_actor, Users.get_actor_for_user(user)}, - %Actor{suspended: true} = actor <- + with %Actor{suspended: true} = actor <- Actors.get_actor_with_preload(id, true), {:delete_tombstones, {_, nil}} <- {:delete_tombstones, Mobilizon.Tombstone.delete_actor_tombstones(id)}, diff --git a/lib/graphql/resolvers/comment.ex b/lib/graphql/resolvers/comment.ex index 26435deb9..386c3fa4d 100644 --- a/lib/graphql/resolvers/comment.ex +++ b/lib/graphql/resolvers/comment.ex @@ -3,7 +3,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Comment do Handles the comment-related GraphQL calls. """ - alias Mobilizon.{Actors, Admin, Discussions, Events, Users} + alias Mobilizon.{Actors, Admin, Discussions, Events} alias Mobilizon.Actors.Actor alias Mobilizon.Discussions.Comment, as: CommentModel alias Mobilizon.Events.{Event, EventOptions} @@ -23,12 +23,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Comment do %{event_id: event_id} = args, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: actor_id} } } ) do - with %Actor{id: actor_id} <- Users.get_actor_for_user(user), - {:find_event, + with {:find_event, {:ok, %Event{ options: %EventOptions{comment_moderation: comment_moderation}, @@ -59,12 +58,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Comment do %{text: text, comment_id: comment_id}, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: actor_id} } } ) do - with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, - %CommentModel{actor_id: comment_actor_id} = comment <- + with %CommentModel{actor_id: comment_actor_id} = comment <- Mobilizon.Discussions.get_comment_with_preload(comment_id), true <- actor_id == comment_actor_id, {:ok, _, %CommentModel{} = comment} <- Comments.update_comment(comment, %{text: text}) do @@ -81,31 +79,34 @@ defmodule Mobilizon.GraphQL.Resolvers.Comment do %{comment_id: comment_id}, %{ context: %{ - current_user: %User{role: role} = user + current_user: %User{role: role}, + current_actor: %Actor{id: actor_id} = actor } } ) do - with {:actor, %Actor{id: actor_id} = actor} <- {:actor, Users.get_actor_for_user(user)}, - %CommentModel{deleted_at: nil} = comment <- - Discussions.get_comment_with_preload(comment_id) do - cond do - {:comment_can_be_managed, true} == CommentModel.can_be_managed_by(comment, actor_id) -> - do_delete_comment(comment, actor) + case Discussions.get_comment_with_preload(comment_id) do + %CommentModel{deleted_at: nil} = comment -> + cond do + {:comment_can_be_managed, true} == CommentModel.can_be_managed_by(comment, actor_id) -> + do_delete_comment(comment, actor) - role in [:moderator, :administrator] -> - with {:ok, res} <- do_delete_comment(comment, actor), - %Actor{} = actor <- Actors.get_actor(actor_id) do - Admin.log_action(actor, "delete", comment) + role in [:moderator, :administrator] -> + with {:ok, res} <- do_delete_comment(comment, actor), + %Actor{} = actor <- Actors.get_actor(actor_id) do + Admin.log_action(actor, "delete", comment) - {:ok, res} - end + {:ok, res} + end + + true -> + {:error, dgettext("errors", "You cannot delete this comment")} + end - true -> - {:error, dgettext("errors", "You cannot delete this comment")} - end - else %CommentModel{deleted_at: deleted_at} when not is_nil(deleted_at) -> {:error, dgettext("errors", "Comment is already deleted")} + + nil -> + {:error, dgettext("errors", "Comment not found")} end end diff --git a/lib/graphql/resolvers/discussion.ex b/lib/graphql/resolvers/discussion.ex index 9603f3cf1..7f1272399 100644 --- a/lib/graphql/resolvers/discussion.ex +++ b/lib/graphql/resolvers/discussion.ex @@ -3,7 +3,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Discussion do Handles the group-related GraphQL calls. """ - alias Mobilizon.{Actors, Discussions, Users} + alias Mobilizon.{Actors, Discussions} alias Mobilizon.Actors.Actor alias Mobilizon.Discussions.{Comment, Discussion} alias Mobilizon.Federation.ActivityPub @@ -17,12 +17,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Discussion do %{page: page, limit: limit}, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: actor_id} } } ) do - with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, - {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, + with {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, {:ok, %Actor{type: :Group} = group} <- Actors.get_group_by_actor_id(group_id) do {:ok, Discussions.find_discussions_for_actor(group, page, limit)} else @@ -37,11 +36,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Discussion do def get_discussion(_parent, %{id: id}, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: creator_id} } }) do - with {:actor, %Actor{id: creator_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, - %Discussion{actor_id: actor_id} = discussion <- + with %Discussion{actor_id: actor_id} = discussion <- Discussions.get_discussion(id), {:member, true} <- {:member, Actors.is_member?(creator_id, actor_id)} do {:ok, discussion} @@ -50,11 +48,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Discussion do def get_discussion(_parent, %{slug: slug}, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: creator_id} } }) do - with {:actor, %Actor{id: creator_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, - %Discussion{actor_id: actor_id} = discussion <- + with %Discussion{actor_id: actor_id} = discussion <- Discussions.get_discussion_by_slug(slug), {:member, true} <- {:member, Actors.is_member?(creator_id, actor_id)} do {:ok, discussion} @@ -89,12 +86,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Discussion do %{title: title, text: text, actor_id: group_id}, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: creator_id} } } ) do - with {:actor, %Actor{id: creator_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, - {:member, true} <- {:member, Actors.is_member?(creator_id, group_id)}, + with {:member, true} <- {:member, Actors.is_member?(creator_id, group_id)}, {:ok, _activity, %Discussion{} = discussion} <- Comments.create_discussion(%{ title: title, @@ -120,12 +116,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Discussion do %{text: text, discussion_id: discussion_id}, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: creator_id} } } ) do - with {:actor, %Actor{id: creator_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, - {:no_discussion, + with {:no_discussion, %Discussion{ actor_id: actor_id, last_comment: %Comment{ @@ -161,12 +156,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Discussion do %{title: title, discussion_id: discussion_id}, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: creator_id} } } ) do - with {:actor, %Actor{id: creator_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, - {:no_discussion, %Discussion{actor_id: actor_id} = discussion} <- + with {:no_discussion, %Discussion{actor_id: actor_id} = discussion} <- {:no_discussion, Discussions.get_discussion(discussion_id)}, {:member, true} <- {:member, Actors.is_member?(creator_id, actor_id)}, {:ok, _activity, %Discussion{} = discussion} <- @@ -187,11 +181,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Discussion do def delete_discussion(_parent, %{discussion_id: discussion_id}, %{ context: %{ - current_user: %User{} = user + current_user: %User{}, + current_actor: %Actor{id: creator_id} = actor } }) do - with {:actor, %Actor{id: creator_id} = actor} <- {:actor, Users.get_actor_for_user(user)}, - {:no_discussion, %Discussion{actor_id: actor_id} = discussion} <- + with {:no_discussion, %Discussion{actor_id: actor_id} = discussion} <- {:no_discussion, Discussions.get_discussion(discussion_id)}, {:member, true} <- {:member, Actors.is_member?(creator_id, actor_id)}, {:ok, _activity, %Discussion{} = discussion} <- diff --git a/lib/graphql/resolvers/event.ex b/lib/graphql/resolvers/event.ex index d38c0a695..dca44e505 100644 --- a/lib/graphql/resolvers/event.ex +++ b/lib/graphql/resolvers/event.ex @@ -3,7 +3,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do Handles the event-related GraphQL calls. """ - alias Mobilizon.{Actors, Admin, Events, Users} + alias Mobilizon.{Actors, Admin, Events} alias Mobilizon.Actors.Actor alias Mobilizon.Config alias Mobilizon.Events.{Event, EventParticipantStats} @@ -24,11 +24,12 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do def organizer_for_event( %Event{attributed_to_id: attributed_to_id, organizer_actor_id: organizer_actor_id}, _args, - %{context: %{current_user: %User{role: user_role} = user}} = _resolution + %{ + context: %{current_user: %User{role: user_role}, current_actor: %Actor{id: actor_id}} + } = _resolution ) when not is_nil(attributed_to_id) do with %Actor{id: group_id} <- Actors.get_actor(attributed_to_id), - %Actor{id: actor_id} <- Users.get_actor_for_user(user), {:member, true} <- {:member, Actors.is_member?(actor_id, group_id) or is_moderator(user_role)}, %Actor{} = actor <- Actors.get_actor(organizer_actor_id) do @@ -77,10 +78,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do defp find_private_event( _parent, %{uuid: uuid}, - %{context: %{current_user: %User{} = user}} = _resolution + %{context: %{current_actor: %Actor{} = profile}} = _resolution ) do - %Actor{} = profile = Users.get_actor_for_user(user) - case Events.get_event_by_uuid_with_preload(uuid) do # Event attributed to group %Event{attributed_to: %Actor{}} = event -> @@ -136,12 +135,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do def list_participants_for_event( %Event{id: event_id} = event, %{page: page, limit: limit, roles: roles}, - %{context: %{current_user: %User{} = user}} = _resolution + %{context: %{current_actor: %Actor{} = actor}} = _resolution ) do - with %Actor{} = actor <- Users.get_actor_for_user(user), - # Check that moderator has right - {:event_can_be_managed, true} <- - {:event_can_be_managed, can_event_be_updated_by?(event, actor)} do + # Check that moderator has right + if can_event_be_updated_by?(event, actor) do roles = case roles do nil -> @@ -160,9 +157,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do participants = Events.list_participants_for_event(event_id, roles, page, limit) {:ok, participants} else - {:event_can_be_managed, _} -> - {:error, - dgettext("errors", "Provided profile doesn't have moderator permissions on this event")} + {:error, + dgettext("errors", "Provided profile doesn't have moderator permissions on this event")} end end @@ -290,13 +286,12 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do def update_event( _parent, %{event_id: event_id} = args, - %{context: %{current_user: %User{} = user}} = _resolution + %{context: %{current_user: %User{} = user, current_actor: %Actor{} = actor}} = _resolution ) do # See https://github.com/absinthe-graphql/absinthe/issues/490 args = Map.put(args, :options, args[:options] || %{}) with {:ok, %Event{} = event} <- Events.get_event_with_preload(event_id), - %Actor{} = actor <- Users.get_actor_for_user(user), {:ok, args} <- verify_profile_change(args, event, user, actor), {:event_can_be_managed, true} <- {:event_can_be_managed, can_event_be_updated_by?(event, actor)}, @@ -335,27 +330,32 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do def delete_event( _parent, %{event_id: event_id}, - %{context: %{current_user: %User{role: role} = user}} + %{ + context: %{ + current_user: %User{role: role}, + current_actor: %Actor{id: actor_id} = actor + } + } ) do - with {:ok, %Event{local: is_local} = event} <- Events.get_event_with_preload(event_id), - %Actor{id: actor_id} = actor <- Users.get_actor_for_user(user) do - cond do - {:event_can_be_managed, true} == - {:event_can_be_managed, can_event_be_deleted_by?(event, actor)} -> - do_delete_event(event, actor) + case Events.get_event_with_preload(event_id) do + {:ok, %Event{local: is_local} = event} -> + cond do + {:event_can_be_managed, true} == + {:event_can_be_managed, can_event_be_deleted_by?(event, actor)} -> + do_delete_event(event, actor) - role in [:moderator, :administrator] -> - with {:ok, res} <- do_delete_event(event, actor, !is_local), - %Actor{} = actor <- Actors.get_actor(actor_id) do - Admin.log_action(actor, "delete", event) + role in [:moderator, :administrator] -> + with {:ok, res} <- do_delete_event(event, actor, !is_local), + %Actor{} = actor <- Actors.get_actor(actor_id) do + Admin.log_action(actor, "delete", event) - {:ok, res} - end + {:ok, res} + end + + true -> + {:error, dgettext("errors", "You cannot delete this event")} + end - true -> - {:error, dgettext("errors", "You cannot delete this event")} - end - else {:error, :event_not_found} -> {:error, dgettext("errors", "Event not found")} end diff --git a/lib/graphql/resolvers/followers.ex b/lib/graphql/resolvers/followers.ex index 950115a5a..c621da59a 100644 --- a/lib/graphql/resolvers/followers.ex +++ b/lib/graphql/resolvers/followers.ex @@ -4,7 +4,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Followers do """ import Mobilizon.Users.Guards - alias Mobilizon.{Actors, Users} + alias Mobilizon.Actors alias Mobilizon.Actors.{Actor, Follower} alias Mobilizon.Federation.ActivityPub alias Mobilizon.Storage.Page @@ -16,17 +16,16 @@ defmodule Mobilizon.GraphQL.Resolvers.Followers do %{page: page, limit: limit} = args, %{ context: %{ - current_user: %User{role: user_role} = user + current_user: %User{role: user_role}, + current_actor: %Actor{id: actor_id} } } ) do - with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, - {:member, true} <- - {:member, Actors.is_moderator?(actor_id, group_id) or is_moderator(user_role)} do + if Actors.is_moderator?(actor_id, group_id) or is_moderator(user_role) do {:ok, Actors.list_paginated_followers_for_actor(group, Map.get(args, :approved), page, limit)} else - _ -> {:error, :unauthorized} + {:error, :unauthorized} end end @@ -35,11 +34,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Followers do @spec update_follower(any(), map(), map()) :: {:ok, Follower.t()} | {:error, any()} def update_follower(_, %{id: follower_id, approved: approved}, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: actor_id} } }) do - with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, - %Follower{target_actor: %Actor{type: :Group, id: group_id}} = follower <- + with %Follower{target_actor: %Actor{type: :Group, id: group_id}} = follower <- Actors.get_follower(follower_id), {:member, true} <- {:member, Actors.is_moderator?(actor_id, group_id)}, diff --git a/lib/graphql/resolvers/group.ex b/lib/graphql/resolvers/group.ex index e5ed6a2f3..c0d25bffa 100644 --- a/lib/graphql/resolvers/group.ex +++ b/lib/graphql/resolvers/group.ex @@ -4,7 +4,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do """ import Mobilizon.Users.Guards - alias Mobilizon.{Actors, Events, Users} + alias Mobilizon.{Actors, Events} alias Mobilizon.Actors.{Actor, Member} alias Mobilizon.Federation.ActivityPub alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor @@ -23,13 +23,12 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do %{preferred_username: name} = args, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: actor_id} } } ) do with {:group, {:ok, %Actor{id: group_id, suspended: false} = group}} <- {:group, ActivityPubActor.find_or_make_group_from_nickname(name)}, - {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)} do {:ok, group} else @@ -119,12 +118,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do args, %{ context: %{ - current_user: user + current_actor: %Actor{id: creator_actor_id} = creator_actor } } ) do - with %Actor{id: creator_actor_id} = creator_actor <- Users.get_actor_for_user(user), - args when is_map(args) <- Map.update(args, :preferred_username, "", &String.downcase/1), + with args when is_map(args) <- Map.update(args, :preferred_username, "", &String.downcase/1), args when is_map(args) <- Map.put(args, :creator_actor, creator_actor), args when is_map(args) <- Map.put(args, :creator_actor_id, creator_actor_id), {:picture, args} when is_map(args) <- {:picture, save_attached_pictures(args)}, @@ -152,12 +150,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do %{id: group_id} = args, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{} = updater_actor } } ) do - with %Actor{} = updater_actor <- Users.get_actor_for_user(user), - {:administrator, true} <- + with {:administrator, true} <- {:administrator, Actors.is_administrator?(updater_actor.id, group_id)}, args when is_map(args) <- Map.put(args, :updater_actor, updater_actor), {:picture, args} when is_map(args) <- {:picture, save_attached_pictures(args)}, @@ -188,12 +185,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do %{group_id: group_id}, %{ context: %{ - current_user: user + current_actor: %Actor{id: actor_id} = actor } } ) do - with %Actor{id: actor_id} = actor <- Users.get_actor_for_user(user), - {:ok, %Actor{} = group} <- Actors.get_group_by_actor_id(group_id), + with {:ok, %Actor{} = group} <- Actors.get_group_by_actor_id(group_id), {:ok, %Member{} = member} <- Actors.get_member(actor_id, group.id), {:is_admin, true} <- {:is_admin, Member.is_administrator(member)}, {:ok, _activity, group} <- ActivityPub.delete(group, actor, true) do @@ -219,10 +215,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do Join an existing group """ def join_group(_parent, %{group_id: group_id} = args, %{ - context: %{current_user: %User{} = user} + context: %{current_actor: %Actor{} = actor} }) do - with %Actor{} = actor <- Users.get_actor_for_user(user), - {:ok, %Actor{type: :Group} = group} <- + with {:ok, %Actor{type: :Group} = group} <- Actors.get_group_by_actor_id(group_id), {:error, :member_not_found} <- Actors.get_member(actor.id, group.id), {:is_able_to_join, true} <- {:is_able_to_join, Member.can_be_joined(group)}, @@ -253,12 +248,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do %{group_id: group_id}, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{} = actor } } ) do - with {:actor, %Actor{} = actor} <- {:actor, Users.get_actor_for_user(user)}, - {:group, %Actor{type: :Group} = group} <- {:group, Actors.get_actor(group_id)}, + with {:group, %Actor{type: :Group} = group} <- {:group, Actors.get_actor(group_id)}, {:ok, _activity, %Member{} = member} <- ActivityPub.leave(group, actor, true) do {:ok, member} else @@ -286,13 +280,12 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do } = args, %{ context: %{ - current_user: %User{role: user_role} = user + current_user: %User{role: user_role}, + current_actor: %Actor{id: actor_id} } } ) do - with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, - {:member, true} <- - {:member, Actors.is_member?(actor_id, group_id) or is_moderator(user_role)} do + if Actors.is_member?(actor_id, group_id) or is_moderator(user_role) do # TODO : Handle public / restricted to group members events {:ok, Events.list_organized_events_for_group( @@ -304,8 +297,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do limit )} else - {:member, false} -> - find_events_for_group(group, args, nil) + find_events_for_group(group, args, nil) end end diff --git a/lib/graphql/resolvers/media.ex b/lib/graphql/resolvers/media.ex index 27f17f94f..9a7ce5d3f 100644 --- a/lib/graphql/resolvers/media.ex +++ b/lib/graphql/resolvers/media.ex @@ -4,7 +4,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Media do """ alias Mobilizon.Actors.Actor - alias Mobilizon.{Medias, Users} + alias Mobilizon.Medias alias Mobilizon.Medias.Media alias Mobilizon.Users.User import Mobilizon.Web.Gettext @@ -44,10 +44,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Media do def upload_media( _parent, %{file: %Plug.Upload{} = file} = args, - %{context: %{current_user: %User{} = user}} + %{context: %{current_actor: %Actor{id: actor_id}}} ) do - with %Actor{id: actor_id} <- Users.get_actor_for_user(user), - {:ok, + with {:ok, %{ name: _name, url: url, diff --git a/lib/graphql/resolvers/member.ex b/lib/graphql/resolvers/member.ex index 8a8738620..fe796a59a 100644 --- a/lib/graphql/resolvers/member.ex +++ b/lib/graphql/resolvers/member.ex @@ -4,7 +4,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do """ import Mobilizon.Users.Guards - alias Mobilizon.{Actors, Users} + alias Mobilizon.Actors alias Mobilizon.Actors.{Actor, Member} alias Mobilizon.Federation.ActivityPub alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor @@ -21,12 +21,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do %Actor{id: group_id} = group, %{page: page, limit: limit, roles: roles}, %{ - context: %{current_user: %User{role: user_role} = user} + context: %{current_user: %User{role: user_role}, current_actor: %Actor{id: actor_id}} } = _resolution ) do - with %Actor{id: actor_id} <- Users.get_actor_for_user(user), - {:member, true} <- - {:member, Actors.is_member?(actor_id, group_id) or is_moderator(user_role)} do + if Actors.is_member?(actor_id, group_id) or is_moderator(user_role) do roles = case roles do "" -> @@ -42,11 +40,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do %Page{} = page = Actors.list_members_for_group(group, roles, page, limit) {:ok, page} else - {:member, false} -> - # Actor is not member of group, fallback to public - with %Page{} = page <- Actors.list_members_for_group(group) do - {:ok, %Page{page | elements: []}} - end + # Actor is not member of group, fallback to public + %Page{} = page = Actors.list_members_for_group(group) + {:ok, %Page{page | elements: []}} end end @@ -59,10 +55,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do def invite_member( _parent, %{group_id: group_id, target_actor_username: target_actor_username}, - %{context: %{current_user: %User{} = user}} + %{context: %{current_actor: %Actor{id: actor_id} = actor}} ) do - with %Actor{id: actor_id} = actor <- Users.get_actor_for_user(user), - {:ok, %Actor{} = group} <- Actors.get_group_by_actor_id(group_id), + with {:ok, %Actor{} = group} <- Actors.get_group_by_actor_id(group_id), {:has_rights_to_invite, {:ok, %Member{role: role}}} when role in [:moderator, :administrator, :creator] <- {:has_rights_to_invite, Actors.get_member(actor_id, group_id)}, @@ -97,9 +92,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do end end - def accept_invitation(_parent, %{id: member_id}, %{context: %{current_user: %User{} = user}}) do - with %Actor{id: actor_id} <- Users.get_actor_for_user(user), - %Member{actor: %Actor{id: member_actor_id}} = member <- + def accept_invitation(_parent, %{id: member_id}, %{ + context: %{current_actor: %Actor{id: actor_id}} + }) do + with %Member{actor: %Actor{id: member_actor_id}} = member <- Actors.get_member(member_id), {:is_same_actor, true} <- {:is_same_actor, member_actor_id == actor_id}, {:ok, _activity, %Member{} = member} <- @@ -115,9 +111,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do end end - def reject_invitation(_parent, %{id: member_id}, %{context: %{current_user: %User{} = user}}) do - with %Actor{id: actor_id} <- Users.get_actor_for_user(user), - {:invitation_exists, %Member{actor: %Actor{id: member_actor_id}} = member} <- + def reject_invitation(_parent, %{id: member_id}, %{ + context: %{current_actor: %Actor{id: actor_id}} + }) do + with {:invitation_exists, %Member{actor: %Actor{id: member_actor_id}} = member} <- {:invitation_exists, Actors.get_member(member_id)}, {:is_same_actor, true} <- {:is_same_actor, member_actor_id == actor_id}, {:ok, _activity, %Member{} = member} <- @@ -137,10 +134,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do end def update_member(_parent, %{member_id: member_id, role: role}, %{ - context: %{current_user: %User{} = user} + context: %{current_actor: %Actor{} = moderator} }) do - with %Actor{} = moderator <- Users.get_actor_for_user(user), - %Member{} = member <- Actors.get_member(member_id), + with %Member{} = member <- Actors.get_member(member_id), {:ok, _activity, %Member{} = member} <- ActivityPub.update(member, %{role: role}, true, %{moderator: moderator}) do {:ok, member} @@ -161,10 +157,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do do: {:error, "You must be logged-in to update a member"} def remove_member(_parent, %{member_id: member_id, group_id: group_id}, %{ - context: %{current_user: %User{} = user} + context: %{current_actor: %Actor{id: moderator_id} = moderator} }) do - with %Actor{id: moderator_id} = moderator <- Users.get_actor_for_user(user), - %Member{role: role} = member when role != :rejected <- Actors.get_member(member_id), + with %Member{role: role} = member when role != :rejected <- Actors.get_member(member_id), %Actor{type: :Group} = group <- Actors.get_actor(group_id), {:has_rights_to_remove, {:ok, %Member{role: role}}} when role in [:moderator, :administrator, :creator] <- diff --git a/lib/graphql/resolvers/participant.ex b/lib/graphql/resolvers/participant.ex index ef2f5527f..f9035dde2 100644 --- a/lib/graphql/resolvers/participant.ex +++ b/lib/graphql/resolvers/participant.ex @@ -2,7 +2,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Participant do @moduledoc """ Handles the participation-related GraphQL calls. """ - alias Mobilizon.{Actors, Config, Crypto, Events, Users} + alias Mobilizon.{Actors, Config, Crypto, Events} alias Mobilizon.Actors.Actor alias Mobilizon.Events.{Event, Participant} alias Mobilizon.GraphQL.API.Participations @@ -225,14 +225,12 @@ defmodule Mobilizon.GraphQL.Resolvers.Participant do %{id: participation_id, role: new_role}, %{ context: %{ - current_user: user + current_actor: %Actor{} = moderator_actor } } ) do - # Check that moderator provided is rightly authenticated - with %Actor{} = moderator_actor <- Users.get_actor_for_user(user), - # Check that participation already exists - {:has_participation, %Participant{role: old_role, event_id: event_id} = participation} <- + # Check that participation already exists + with {:has_participation, %Participant{role: old_role, event_id: event_id} = participation} <- {:has_participation, Events.get_participant(participation_id)}, {:same_role, false} <- {:same_role, new_role == old_role}, # Check that moderator has right diff --git a/lib/graphql/resolvers/person.ex b/lib/graphql/resolvers/person.ex index b8818fae3..ff3a36f1d 100644 --- a/lib/graphql/resolvers/person.ex +++ b/lib/graphql/resolvers/person.ex @@ -91,8 +91,14 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do @doc """ Returns the current actor for the currently logged-in user """ - def get_current_person(_parent, _args, %{context: %{current_user: user}}) do - {:ok, Users.get_actor_for_user(user)} + @spec get_current_person(any, any, Absinthe.Resolution.t()) :: + {:error, :unauthenticated} | {:ok, Actor.t()} + def get_current_person(_parent, _args, %{context: %{current_actor: %Actor{} = actor}}) do + {:ok, actor} + end + + def get_current_person(_parent, _args, %{context: %{current_user: %User{}}}) do + {:error, :no_current_person} end def get_current_person(_parent, _args, _resolution) do @@ -102,6 +108,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do @doc """ Returns the list of identities for the logged-in user """ + @spec identities(any, any, Absinthe.Resolution.t()) :: + {:error, :unauthenticated} | {:ok, list(Actor.t())} def identities(_parent, _args, %{context: %{current_user: user}}) do {:ok, Users.get_actors_for_user(user)} end @@ -148,21 +156,24 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do require Logger args = Map.put(args, :user_id, user.id) - with {:find_actor, %Actor{} = actor} <- - {:find_actor, Actors.get_actor(id)}, - {:is_owned, %Actor{}} <- User.owns_actor(user, actor.id), - {:picture, args} when is_map(args) <- {:picture, save_attached_pictures(args)}, - {:ok, _activity, %Actor{} = actor} <- ActivityPub.update(actor, args, true) do - {:ok, actor} - else - {:picture, {:error, :file_too_large}} -> - {:error, dgettext("errors", "The provided picture is too heavy")} + case owned_actor(user, id) do + {:ok, %Actor{} = actor} -> + case save_attached_pictures(args) do + args when is_map(args) -> + case ActivityPub.update(actor, args, true) do + {:ok, _activity, %Actor{} = actor} -> + {:ok, actor} - {:find_actor, nil} -> - {:error, dgettext("errors", "Profile not found")} + {:error, err} -> + {:error, err} + end - {:is_owned, nil} -> - {:error, dgettext("errors", "Profile is not owned by authenticated user")} + {:error, :file_too_large} -> + {:error, dgettext("errors", "The provided picture is too heavy")} + end + + {:error, err} -> + {:error, err} end end @@ -176,27 +187,22 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do def delete_person( _parent, %{id: id} = _args, - %{context: %{current_user: user}} = _resolution + %{context: %{current_user: %User{} = user}} = _resolution ) do - with {:find_actor, %Actor{} = actor} <- - {:find_actor, Actors.get_actor(id)}, - {:is_owned, %Actor{}} <- User.owns_actor(user, actor.id), - {:last_identity, false} <- {:last_identity, last_identity?(user)}, - {:last_admin, false} <- {:last_admin, last_admin_of_a_group?(actor.id)}, - {:ok, actor} <- Actors.delete_actor(actor) do - {:ok, actor} - else - {:find_actor, nil} -> - {:error, dgettext("errors", "Profile not found")} + case owned_actor(user, id) do + {:ok, %Actor{} = actor} -> + if last_identity?(user) do + {:error, dgettext("errors", "Cannot remove the last identity of a user")} + else + if last_admin_of_a_group?(actor.id) do + {:error, dgettext("errors", "Cannot remove the last administrator of a group")} + else + Actors.delete_actor(actor) + end + end - {:last_identity, true} -> - {:error, dgettext("errors", "Cannot remove the last identity of a user")} - - {:last_admin, true} -> - {:error, dgettext("errors", "Cannot remove the last administrator of a group")} - - {:is_owned, nil} -> - {:error, dgettext("errors", "Profile is not owned by authenticated user")} + {:error, err} -> + {:error, err} end end @@ -204,31 +210,65 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do {:error, :unauthenticated} end + @spec owned_actor(User.t(), integer() | String.t()) :: {:error, String.t()} | {:ok, Actor.t()} + defp owned_actor(%User{} = user, actor_id) do + with {:find_actor, %Actor{} = actor} <- + {:find_actor, Actors.get_actor(actor_id)}, + {:is_owned, %Actor{}} <- User.owns_actor(user, actor.id) do + {:ok, actor} + else + {:find_actor, nil} -> + {:error, dgettext("errors", "Profile not found")} + + {:is_owned, nil} -> + {:error, dgettext("errors", "Profile is not owned by authenticated user")} + end + end + defp last_identity?(user) do length(Users.get_actors_for_user(user)) <= 1 end + @spec save_attached_pictures(map()) :: map() | {:error, any()} defp save_attached_pictures(args) do - with args when is_map(args) <- save_attached_picture(args, :avatar), - args when is_map(args) <- save_attached_picture(args, :banner) do - args + case save_attached_picture(args, :avatar) do + {:error, err} -> + {:error, err} + + args when is_map(args) -> + case save_attached_picture(args, :banner) do + {:error, err} -> + {:error, err} + + args when is_map(args) -> + args + end end end + @spec save_attached_picture(map(), :avatar | :banner) :: map() | {:error, any} defp save_attached_picture(args, key) do if Map.has_key?(args, key) && !is_nil(args[key][:media]) do - with media when is_map(media) <- save_picture(args[key][:media], key) do - Map.put(args, key, media) + case save_picture(args[key][:media], key) do + {:error, err} -> + {:error, err} + + media when is_map(media) -> + Map.put(args, key, media) end else args end end + @spec save_picture(map(), :avatar | :banner) :: {:ok, map()} | {:error, any()} defp save_picture(media, key) do - with {:ok, %{name: name, url: url, content_type: content_type, size: size}} <- - Upload.store(media.file, type: key, description: media.alt) do - %{"name" => name, "url" => url, "content_type" => content_type, "size" => size} + case Upload.store(media.file, type: key, description: media.alt) do + {:ok, %{name: name, url: url, content_type: content_type, size: size}} -> + %{"name" => name, "url" => url, "content_type" => content_type, "size" => size} + + {:error, err} -> + {:error, err} end end @@ -237,30 +277,37 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do """ def register_person(_parent, args, _resolution) do # When registering, email is assumed confirmed (unlike changing email) - with {:ok, %User{} = user} <- Users.get_user_by_email(args.email, unconfirmed: false), - user_actor <- Users.get_actor_for_user(user), - no_actor <- is_nil(user_actor), - {:no_actor, true} <- {:no_actor, no_actor}, - args <- Map.update(args, :preferred_username, "", &String.downcase/1), - args <- Map.put(args, :user_id, user.id), - {:picture, args} when is_map(args) <- {:picture, save_attached_pictures(args)}, - {:ok, %Actor{} = new_person} <- Actors.new_person(args, true) do - {:ok, new_person} - else - {:picture, {:error, :file_too_large}} -> - {:error, dgettext("errors", "The provided picture is too heavy")} + case Users.get_user_by_email(args.email, unconfirmed: false) do + {:ok, %User{} = user} -> + if is_nil(Users.get_actor_for_user(user)) do + # No profile yet, we can create one + case prepare_args(args, user) do + args when is_map(args) -> + Actors.new_person(args, true) + + {:error, :file_too_large} -> + {:error, dgettext("errors", "The provided picture is too heavy")} + + {:error, _err} -> + {:error, dgettext("errors", "Error while uploading pictures")} + end + else + {:error, dgettext("errors", "You already have a profile for this user")} + end {:error, :user_not_found} -> {:error, dgettext("errors", "No user with this email was found")} - - {:no_actor, _} -> - {:error, dgettext("errors", "You already have a profile for this user")} - - {:error, %Ecto.Changeset{} = e} -> - {:error, e} end end + @spec prepare_args(map(), User.t()) :: map() | {:error, any()} + defp prepare_args(args, %User{} = user) do + args + |> Map.update(:preferred_username, "", &String.downcase/1) + |> Map.put(:user_id, user.id) + |> save_attached_pictures() + end + @doc """ Returns the participations, optionally restricted to an event """ @@ -269,17 +316,13 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do %{event_id: event_id}, %{context: %{current_user: %User{} = user}} ) do - with {:can_get_participations, true} <- - {:can_get_participations, user_can_access_person_details?(person, user)}, - {:no_participant, {:ok, %Participant{} = participant}} <- - {:no_participant, Events.get_participant(event_id, actor_id)} do - {:ok, %Page{elements: [participant], total: 1}} + if user_can_access_person_details?(person, user) do + case Events.get_participant(event_id, actor_id) do + {:ok, %Participant{} = participant} -> {:ok, %Page{elements: [participant], total: 1}} + {:error, :participant_not_found} -> {:ok, %Page{elements: [], total: 0}} + end else - {:is_owned, nil} -> - {:error, dgettext("errors", "Profile is not owned by authenticated user")} - - {:no_participant, _} -> - {:ok, %Page{elements: [], total: 0}} + {:error, :unauthorized} end end diff --git a/lib/graphql/resolvers/post.ex b/lib/graphql/resolvers/post.ex index 0ea6b21e1..25acc130e 100644 --- a/lib/graphql/resolvers/post.ex +++ b/lib/graphql/resolvers/post.ex @@ -4,7 +4,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Post do """ import Mobilizon.Users.Guards - alias Mobilizon.{Actors, Posts, Users} + alias Mobilizon.{Actors, Posts} alias Mobilizon.Actors.Actor alias Mobilizon.Federation.ActivityPub alias Mobilizon.Federation.ActivityPub.{Permission, Utils} @@ -27,12 +27,12 @@ defmodule Mobilizon.GraphQL.Resolvers.Post do %{page: page, limit: limit} = args, %{ context: %{ - current_user: %User{role: user_role} = user + current_user: %User{role: user_role}, + current_actor: %Actor{id: actor_id} } } = _resolution ) do - with %Actor{id: actor_id} <- Users.get_actor_for_user(user), - {:member, true} <- + with {:member, true} <- {:member, Actors.is_member?(actor_id, group_id) or is_moderator(user_role)}, %Page{} = page <- Posts.get_posts_for_group(group, page, limit) do {:ok, page} @@ -65,13 +65,12 @@ defmodule Mobilizon.GraphQL.Resolvers.Post do %{slug: slug}, %{ context: %{ - current_user: %User{role: user_role} = user + current_user: %User{role: user_role}, + current_actor: %Actor{} = current_profile } } = _resolution ) do - with {:current_actor, %Actor{} = current_profile} <- - {:current_actor, Users.get_actor_for_user(user)}, - {:post, %Post{attributed_to: %Actor{}} = post} <- + with {:post, %Post{attributed_to: %Actor{}} = post} <- {:post, Posts.get_post_by_slug_with_preloads(slug)}, {:member, true} <- {:member, @@ -107,12 +106,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Post do %{attributed_to_id: group_id} = args, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: actor_id} } } = _resolution ) do - with %Actor{id: actor_id} <- Users.get_actor_for_user(user), - {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, + with {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, %Actor{} = group <- Actors.get_actor(group_id), args <- Map.update(args, :picture, nil, fn picture -> @@ -147,12 +145,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Post do %{id: id} = args, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: actor_id, url: actor_url} } } = _resolution ) do with {:uuid, {:ok, _uuid}} <- {:uuid, Ecto.UUID.cast(id)}, - %Actor{id: actor_id, url: actor_url} <- Users.get_actor_for_user(user), {:post, %Post{attributed_to: %Actor{id: group_id} = group} = post} <- {:post, Posts.get_post_with_preloads(id)}, args <- @@ -185,12 +182,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Post do %{id: post_id}, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: actor_id} = actor } } = _resolution ) do with {:uuid, {:ok, _uuid}} <- {:uuid, Ecto.UUID.cast(post_id)}, - %Actor{id: actor_id} = actor <- Users.get_actor_for_user(user), {:post, %Post{attributed_to: %Actor{id: group_id}} = post} <- {:post, Posts.get_post_with_preloads(post_id)}, {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, diff --git a/lib/graphql/resolvers/report.ex b/lib/graphql/resolvers/report.ex index feed38ec2..13114273f 100644 --- a/lib/graphql/resolvers/report.ex +++ b/lib/graphql/resolvers/report.ex @@ -5,7 +5,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Report do import Mobilizon.Users.Guards - alias Mobilizon.{Actors, Config, Reports, Users} + alias Mobilizon.{Actors, Config, Reports} alias Mobilizon.Actors.Actor alias Mobilizon.Reports.{Note, Report} alias Mobilizon.Users.User @@ -47,13 +47,12 @@ defmodule Mobilizon.GraphQL.Resolvers.Report do def create_report( _parent, args, - %{context: %{current_user: %User{} = user}} = _resolution + %{context: %{current_actor: %Actor{id: reporter_id}}} = _resolution ) do - with %Actor{id: reporter_id} <- Users.get_actor_for_user(user), - {:ok, _, %Report{} = report} <- - args |> Map.put(:reporter_id, reporter_id) |> API.Reports.report() do - {:ok, report} - else + case args |> Map.put(:reporter_id, reporter_id) |> API.Reports.report() do + {:ok, _, %Report{} = report} -> + {:ok, report} + _error -> {:error, dgettext("errors", "Error while saving report")} end @@ -84,11 +83,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Report do def update_report( _parent, %{report_id: report_id, status: status}, - %{context: %{current_user: %User{role: role} = user}} + %{context: %{current_user: %User{role: role}, current_actor: %Actor{} = actor}} ) when is_moderator(role) do - with %Actor{} = actor <- Users.get_actor_for_user(user), - %Report{} = report <- Mobilizon.Reports.get_report(report_id), + with %Report{} = report <- Mobilizon.Reports.get_report(report_id), {:ok, %Report{} = report} <- API.Reports.update_report_status(actor, report, status) do {:ok, report} else @@ -104,11 +102,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Report do def create_report_note( _parent, %{report_id: report_id, content: content}, - %{context: %{current_user: %User{role: role} = user}} + %{context: %{current_user: %User{role: role}, current_actor: %Actor{id: moderator_id}}} ) when is_moderator(role) do - with %Actor{id: moderator_id} <- Users.get_actor_for_user(user), - %Report{} = report <- Reports.get_report(report_id), + with %Report{} = report <- Reports.get_report(report_id), %Actor{} = moderator <- Actors.get_local_actor_with_preload(moderator_id), {:ok, %Note{} = note} <- API.Reports.create_report_note(report, moderator, content) do {:ok, note} @@ -118,11 +115,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Report do def delete_report_note( _parent, %{note_id: note_id}, - %{context: %{current_user: %User{role: role} = user}} + %{context: %{current_user: %User{role: role}, current_actor: %Actor{id: moderator_id}}} ) when is_moderator(role) do - with %Actor{id: moderator_id} <- Users.get_actor_for_user(user), - %Note{} = note <- Reports.get_note(note_id), + with %Note{} = note <- Reports.get_note(note_id), %Actor{} = moderator <- Actors.get_local_actor_with_preload(moderator_id), {:ok, %Note{} = note} <- API.Reports.delete_report_note(note, moderator) do {:ok, %{id: note.id}} diff --git a/lib/graphql/resolvers/resource.ex b/lib/graphql/resolvers/resource.ex index 3aa7b5322..8e3783a8d 100644 --- a/lib/graphql/resolvers/resource.ex +++ b/lib/graphql/resolvers/resource.ex @@ -3,7 +3,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Resource do Handles the resources-related GraphQL calls """ - alias Mobilizon.{Actors, Resources, Users} + alias Mobilizon.{Actors, Resources} alias Mobilizon.Actors.Actor alias Mobilizon.Federation.ActivityPub alias Mobilizon.Resources.Resource @@ -26,12 +26,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Resource do %{page: page, limit: limit}, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: actor_id} } } = _resolution ) do - with %Actor{id: actor_id} <- Users.get_actor_for_user(user), - {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, + with {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, %Page{} = page <- Resources.get_resources_for_group(group, page, limit) do {:ok, page} else @@ -53,12 +52,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Resource do %{page: page, limit: limit}, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: actor_id} } } = _resolution ) do - with %Actor{id: actor_id} <- Users.get_actor_for_user(user), - {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, + with {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, %Page{} = page <- Resources.get_resources_for_folder(parent, page, limit) do {:ok, page} end @@ -72,13 +70,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Resource do %{path: path, username: username}, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: actor_id} } } = _resolution ) do - with {:current_actor, %Actor{id: actor_id}} <- - {:current_actor, Users.get_actor_for_user(user)}, - {:group, %Actor{id: group_id}} <- {:group, Actors.get_actor_by_name(username, :Group)}, + with {:group, %Actor{id: group_id}} <- {:group, Actors.get_actor_by_name(username, :Group)}, {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, {:resource, %Resource{} = resource} <- {:resource, Resources.get_resource_by_group_and_path_with_preloads(group_id, path)} do @@ -99,12 +95,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Resource do %{actor_id: group_id} = args, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: actor_id} } } = _resolution ) do - with %Actor{id: actor_id} <- Users.get_actor_for_user(user), - {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, + with {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, parent <- get_eventual_parent(args), {:own_check, true} <- {:own_check, check_resource_owned_by_group(parent, group_id)}, {:ok, _, %Resource{} = resource} <- @@ -138,12 +133,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Resource do %{id: resource_id} = args, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: actor_id, url: actor_url} } } = _resolution ) do - with %Actor{id: actor_id, url: actor_url} <- Users.get_actor_for_user(user), - {:resource, %Resource{actor_id: group_id} = resource} <- + with {:resource, %Resource{actor_id: group_id} = resource} <- {:resource, Resources.get_resource_with_preloads(resource_id)}, {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, {:ok, _, %Resource{} = resource} <- @@ -167,12 +161,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Resource do %{id: resource_id}, %{ context: %{ - current_user: %User{} = user + current_actor: %Actor{id: actor_id} = actor } } = _resolution ) do - with %Actor{id: actor_id} = actor <- Users.get_actor_for_user(user), - {:resource, %Resource{parent_id: _parent_id, actor_id: group_id} = resource} <- + with {:resource, %Resource{parent_id: _parent_id, actor_id: group_id} = resource} <- {:resource, Resources.get_resource_with_preloads(resource_id)}, {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, {:ok, _, %Resource{} = resource} <- diff --git a/lib/graphql/resolvers/todos.ex b/lib/graphql/resolvers/todos.ex index 825500531..13e4cd161 100644 --- a/lib/graphql/resolvers/todos.ex +++ b/lib/graphql/resolvers/todos.ex @@ -3,12 +3,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Todos do Handles the todos related GraphQL calls """ - alias Mobilizon.{Actors, Todos, Users} + alias Mobilizon.{Actors, Todos} alias Mobilizon.Actors.Actor alias Mobilizon.Federation.ActivityPub alias Mobilizon.Storage.Page alias Mobilizon.Todos.{Todo, TodoList} - alias Mobilizon.Users.User import Mobilizon.Web.Gettext require Logger @@ -22,11 +21,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Todos do %Actor{id: group_id} = group, _args, %{ - context: %{current_user: %User{} = user} + context: %{current_actor: %Actor{id: actor_id}} } = _resolution ) do - with %Actor{id: actor_id} <- Users.get_actor_for_user(user), - {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, + with {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, %Page{} = page <- Todos.get_todo_lists_for_group(group) do {:ok, page} else @@ -45,11 +43,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Todos do %TodoList{actor_id: group_id} = todo_list, _args, %{ - context: %{current_user: %User{} = user} + context: %{current_actor: %Actor{id: actor_id}} } = _resolution ) do - with %Actor{id: actor_id} <- Users.get_actor_for_user(user), - {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, + with {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, %Page{} = page <- Todos.get_todos_for_todo_list(todo_list) do {:ok, page} else @@ -62,11 +59,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Todos do _parent, %{id: todo_list_id}, %{ - context: %{current_user: %User{} = user} + context: %{current_actor: %Actor{id: actor_id}} } = _resolution ) do - with {:actor, %Actor{id: actor_id}} <- {:actor, Users.get_actor_for_user(user)}, - {:todo, %TodoList{actor_id: group_id} = todo} <- + with {:todo, %TodoList{actor_id: group_id} = todo} <- {:todo, Todos.get_todo_list(todo_list_id)}, {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)} do {:ok, todo} @@ -86,11 +82,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Todos do _parent, %{group_id: group_id} = args, %{ - context: %{current_user: %User{} = user} + context: %{current_actor: %Actor{id: actor_id}} } = _resolution ) do - with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, - {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, + with {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, {:ok, _, %TodoList{} = todo_list} <- ActivityPub.create(:todo_list, Map.put(args, :actor_id, group_id), true, %{}) do {:ok, todo_list} @@ -153,11 +148,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Todos do _parent, %{id: todo_id}, %{ - context: %{current_user: %User{} = user} + context: %{current_actor: %Actor{id: actor_id}} } = _resolution ) do - with {:actor, %Actor{id: actor_id}} <- {:actor, Users.get_actor_for_user(user)}, - {:todo, %Todo{todo_list_id: todo_list_id} = todo} <- + with {:todo, %Todo{todo_list_id: todo_list_id} = todo} <- {:todo, Todos.get_todo(todo_id)}, {:todo_list, %TodoList{actor_id: group_id}} <- {:todo_list, Todos.get_todo_list(todo_list_id)}, @@ -179,11 +173,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Todos do _parent, %{todo_list_id: todo_list_id} = args, %{ - context: %{current_user: %User{} = user} + context: %{current_actor: %Actor{id: actor_id}} } = _resolution ) do - with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, - {:todo_list, %TodoList{actor_id: group_id} = _todo_list} <- + with {:todo_list, %TodoList{actor_id: group_id} = _todo_list} <- {:todo_list, Todos.get_todo_list(todo_list_id)}, {:member, true} <- {:member, Actors.is_member?(actor_id, group_id)}, {:ok, _, %Todo{} = todo} <- @@ -205,11 +198,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Todos do _parent, %{id: todo_id} = args, %{ - context: %{current_user: %User{} = user} + context: %{current_actor: %Actor{id: actor_id}} } = _resolution ) do - with {:actor, %Actor{id: actor_id} = _actor} <- {:actor, Users.get_actor_for_user(user)}, - {:todo, %Todo{todo_list_id: todo_list_id} = todo} <- + with {:todo, %Todo{todo_list_id: todo_list_id} = todo} <- {:todo, Todos.get_todo(todo_id)}, {:todo_list, %TodoList{actor_id: group_id}} <- {:todo_list, Todos.get_todo_list(todo_list_id)}, diff --git a/lib/graphql/resolvers/user.ex b/lib/graphql/resolvers/user.ex index 803f5138f..b9287529a 100644 --- a/lib/graphql/resolvers/user.ex +++ b/lib/graphql/resolvers/user.ex @@ -33,8 +33,7 @@ defmodule Mobilizon.GraphQL.Resolvers.User do """ @spec get_current_user(any, map(), Absinthe.Resolution.t()) :: {:error, :unauthenticated} | {:ok, Mobilizon.Users.User.t()} - def get_current_user(_parent, _args, %{context: %{current_user: %User{} = user} = context}) do - # Logger.error(inspect(context)) + def get_current_user(_parent, _args, %{context: %{current_user: %User{} = user}}) do {:ok, user} end @@ -204,23 +203,27 @@ defmodule Mobilizon.GraphQL.Resolvers.User do """ @spec validate_user(map(), %{token: String.t()}, map()) :: {:ok, map()} | {:error, String.t()} def validate_user(_parent, %{token: token}, _resolution) do - with {:check_confirmation_token, {:ok, %User{} = user}} <- - {:check_confirmation_token, Email.User.check_confirmation_token(token)}, - {:get_actor, actor} <- {:get_actor, Users.get_actor_for_user(user)} do - {:ok, %{access_token: access_token, refresh_token: refresh_token}} = - Authenticator.generate_tokens(user) + case Email.User.check_confirmation_token(token) do + {:ok, %User{} = user} -> + actor = Users.get_actor_for_user(user) - {:ok, - %{ - access_token: access_token, - refresh_token: refresh_token, - user: Map.put(user, :default_actor, actor) - }} - else - error -> + {:ok, %{access_token: access_token, refresh_token: refresh_token}} = + Authenticator.generate_tokens(user) + + {:ok, + %{ + access_token: access_token, + refresh_token: refresh_token, + user: Map.put(user, :default_actor, actor) + }} + + {:error, :invalid_token} -> + Logger.info("Invalid token #{token} to validate user") + {:error, dgettext("errors", "Unable to validate user")} + + {:error, %Ecto.Changeset{} = err} -> Logger.info("Unable to validate user with token #{token}") - Logger.debug(inspect(error)) - + Logger.debug(inspect(err)) {:error, dgettext("errors", "Unable to validate user")} end end @@ -280,8 +283,17 @@ defmodule Mobilizon.GraphQL.Resolvers.User do {:ok, tokens} = Authenticator.authenticate(email, password) {:ok, Map.put(tokens, :user, user)} - {:error, error} -> - {:error, error} + {:error, %Ecto.Changeset{errors: [password: {"registration.error.password_too_short", _}]}} -> + {:error, + gettext( + "The password you have choosen is too short. Please make sure your password contains at least 6 charaters." + )} + + {:error, _err} -> + {:error, + gettext( + "The token you provided is invalid. Make sure that the URL is exactly the one provided inside the email you got." + )} end end @@ -289,12 +301,12 @@ defmodule Mobilizon.GraphQL.Resolvers.User do def change_default_actor( _parent, %{preferred_username: username}, - %{context: %{current_user: user}} + %{context: %{current_user: %User{id: user_id} = user}} ) do - with %Actor{id: actor_id} <- Actors.get_local_actor_by_name(username), + with %Actor{id: actor_id} = actor <- Actors.get_local_actor_by_name(username), {:user_actor, true} <- {:user_actor, actor_id in Enum.map(Users.get_actors_for_user(user), & &1.id)}, - %User{} = user <- Users.update_user_default_actor(user.id, actor_id) do + %User{} = user <- Users.update_user_default_actor(user_id, actor) do {:ok, user} else {:user_actor, _} -> @@ -459,19 +471,17 @@ defmodule Mobilizon.GraphQL.Resolvers.User do end def delete_account(_parent, %{user_id: user_id}, %{ - context: %{current_user: %User{role: role} = moderator_user} + context: %{ + current_user: %User{role: role}, + current_actor: %Actor{} = moderator_actor + } }) when is_moderator(role) do - with {:moderator_actor, %Actor{} = moderator_actor} <- - {:moderator_actor, Users.get_actor_for_user(moderator_user)}, - %User{disabled: false} = user <- Users.get_user(user_id), + with %User{disabled: false} = user <- Users.get_user(user_id), {:ok, %User{}} <- do_delete_account(%User{} = user, actor_performing: Relay.get_actor()) do Admin.log_action(moderator_actor, "delete", user) else - {:moderator_actor, nil} -> - {:error, dgettext("errors", "No profile found for the moderator user")} - %User{disabled: true} -> {:error, dgettext("errors", "User already disabled")} end diff --git a/lib/graphql/schema.ex b/lib/graphql/schema.ex index 74f3f9a38..4331be265 100644 --- a/lib/graphql/schema.ex +++ b/lib/graphql/schema.ex @@ -20,7 +20,7 @@ defmodule Mobilizon.GraphQL.Schema do alias Mobilizon.Actors.{Actor, Follower, Member} alias Mobilizon.Discussions.Comment alias Mobilizon.Events.{Event, Participant} - alias Mobilizon.GraphQL.Middleware.ErrorHandler + alias Mobilizon.GraphQL.Middleware.{CurrentActorProvider, ErrorHandler} alias Mobilizon.GraphQL.Schema alias Mobilizon.Storage.Repo @@ -195,7 +195,7 @@ defmodule Mobilizon.GraphQL.Schema do end def middleware(middleware, _field, %{identifier: type}) when type in [:query, :mutation] do - middleware ++ [ErrorHandler] + [CurrentActorProvider] ++ middleware ++ [ErrorHandler] end def middleware(middleware, _field, _object) do diff --git a/lib/mobilizon.ex b/lib/mobilizon.ex index 59f98f7af..a701de012 100644 --- a/lib/mobilizon.ex +++ b/lib/mobilizon.ex @@ -59,6 +59,7 @@ defmodule Mobilizon do cachex_spec(:config, 10, 60, 60), cachex_spec(:rich_media_cache, 10, 60, 60), cachex_spec(:activity_pub, 2500, 3, 15), + cachex_spec(:default_actors, 2500, 3, 15), %{ id: :cache_key_value, start: {Cachex, :start_link, [:key_value]} diff --git a/lib/mobilizon/events/events.ex b/lib/mobilizon/events/events.ex index fdd1579b5..2e7d9a707 100644 --- a/lib/mobilizon/events/events.ex +++ b/lib/mobilizon/events/events.ex @@ -739,6 +739,8 @@ defmodule Mobilizon.Events do """ @spec get_participant(integer) :: Participant.t() | nil + @spec get_participant(integer | String.t(), integer | String.t(), map()) :: + {:ok, Participant.t()} | {:error, :participant_not_found} def get_participant(participant_id) do Participant |> where([p], p.id == ^participant_id) @@ -749,8 +751,6 @@ defmodule Mobilizon.Events do @doc """ Gets a single participation for an event and actor. """ - @spec get_participant(integer | String.t(), integer | String.t(), map()) :: - {:ok, Participant.t()} | {:error, :participant_not_found} def get_participant(event_id, actor_id, params \\ %{}) # This one if to check someone doesn't go to the same event twice diff --git a/lib/mobilizon/users/users.ex b/lib/mobilizon/users/users.ex index b19ecffd3..7a8d2fd24 100644 --- a/lib/mobilizon/users/users.ex +++ b/lib/mobilizon/users/users.ex @@ -117,7 +117,7 @@ defmodule Mobilizon.Users do @doc """ Get an user by its reset password token. """ - @spec get_user_by_reset_password_token(String.t()) :: Actor.t() + @spec get_user_by_reset_password_token(String.t()) :: Actor.t() | nil def get_user_by_reset_password_token(token) do token |> user_by_reset_password_token_query() @@ -263,22 +263,26 @@ defmodule Mobilizon.Users do Updates user's default actor. Raises `Ecto.NoResultsError` if the user does not exist. """ - @spec update_user_default_actor(integer | String.t(), integer | String.t()) :: User.t() - def update_user_default_actor(user_id, actor_id) do - with _ <- - user_id - |> update_user_default_actor_query(actor_id) - |> Repo.update_all([]) do - user_id - |> get_user!() - |> Repo.preload([:default_actor]) - end + @spec update_user_default_actor(integer | String.t(), Actor.t() | nil) :: User.t() + def update_user_default_actor(user_id, actor) do + actor_id = if is_nil(actor), do: nil, else: actor.id + + user_id + |> update_user_default_actor_query(actor_id) + |> Repo.update_all([]) + + Cachex.put(:default_actors, to_string(user_id), actor) + + user_id + |> get_user!() + |> Repo.preload([:default_actor]) end @doc """ Returns the list of users. """ - @spec list_users(String.t(), integer | nil, integer | nil, atom | nil, atom | nil) :: Page.t() + @spec list_users(String.t(), integer | nil, integer | nil, atom | nil, atom | nil) :: + Page.t(User.t()) def list_users(email \\ "", page \\ nil, limit \\ nil, sort \\ nil, direction \\ nil) def list_users("", page, limit, sort, direction) do @@ -596,7 +600,7 @@ defmodule Mobilizon.Users do from(a in Actor, where: a.user_id == ^user_id) end - @spec update_user_default_actor_query(integer | String.t(), integer | String.t()) :: + @spec update_user_default_actor_query(integer | String.t(), integer | String.t() | nil) :: Ecto.Query.t() defp update_user_default_actor_query(user_id, actor_id) do from( diff --git a/lib/service/actor_suspension.ex b/lib/service/actor_suspension.ex index 104981824..e5f104eb2 100644 --- a/lib/service/actor_suspension.ex +++ b/lib/service/actor_suspension.ex @@ -197,13 +197,12 @@ defmodule Mobilizon.Service.ActorSuspension do defp reset_default_actor_id(%Actor{type: :Person, user: %User{id: user_id} = user, id: actor_id}) do Logger.debug("reset_default_actor_id") - new_actor_id = + new_actor = user |> Users.get_actors_for_user() - |> Enum.map(& &1.id) - |> Enum.find(&(&1 !== actor_id)) + |> Enum.find(&(&1.id !== actor_id)) - {:ok, Users.update_user_default_actor(user_id, new_actor_id)} + {:ok, Users.update_user_default_actor(user_id, new_actor)} rescue _e in Ecto.NoResultsError -> {:error, :user_not_found} diff --git a/lib/web/email/user.ex b/lib/web/email/user.ex index 29887c358..66ccb2137 100644 --- a/lib/web/email/user.ex +++ b/lib/web/email/user.ex @@ -7,7 +7,7 @@ defmodule Mobilizon.Web.Email.User do import Bamboo.Phoenix - import Mobilizon.Web.Gettext, only: [gettext: 1, gettext: 2] + import Mobilizon.Web.Gettext, only: [gettext: 2] alias Mobilizon.{Config, Crypto, Users} alias Mobilizon.Storage.Repo @@ -57,20 +57,26 @@ defmodule Mobilizon.Web.Email.User do |> render(:password_reset) end - @spec check_confirmation_token(String.t()) :: {:ok, User.t()} | {:error, :invalid_token} + @spec check_confirmation_token(String.t()) :: + {:ok, User.t()} | {:error, :invalid_token | Ecto.Changeset.t()} def check_confirmation_token(token) when is_binary(token) do - with %User{} = user <- Users.get_user_by_activation_token(token), - {:ok, %User{} = user} <- - Users.update_user(user, %{ - confirmed_at: DateTime.utc_now() |> DateTime.truncate(:second), - confirmation_sent_at: nil, - confirmation_token: nil, - email: user.unconfirmed_email || user.email - }) do - Logger.info("User #{user.email} has been confirmed") - {:ok, user} - else - _err -> + case Users.get_user_by_activation_token(token) do + %User{} = user -> + case Users.update_user(user, %{ + confirmed_at: DateTime.utc_now() |> DateTime.truncate(:second), + confirmation_sent_at: nil, + confirmation_token: nil, + email: user.unconfirmed_email || user.email + }) do + {:ok, %User{} = user} -> + Logger.info("User #{user.email} has been confirmed") + {:ok, user} + + {:error, %Ecto.Changeset{} = err} -> + {:error, err} + end + + nil -> {:error, :invalid_token} end end @@ -98,30 +104,20 @@ defmodule Mobilizon.Web.Email.User do Check that the provided token is correct and update provided password """ @spec check_reset_password_token(String.t(), String.t()) :: - {:ok, User.t()} | {:error, String.t()} + {:ok, User.t()} | {:error, :user_not_found | Ecto.Changeset.t()} def check_reset_password_token(password, token) do - with %User{} = user <- Users.get_user_by_reset_password_token(token), - {:ok, %User{} = user} <- - Repo.update( - User.password_reset_changeset(user, %{ - "password" => password, - "reset_password_sent_at" => nil, - "reset_password_token" => nil - }) - ) do - {:ok, user} - else - {:error, %Ecto.Changeset{errors: [password: {"registration.error.password_too_short", _}]}} -> - {:error, - gettext( - "The password you have choosen is too short. Please make sure your password contains at least 6 charaters." - )} + case Users.get_user_by_reset_password_token(token) do + %User{} = user -> + user + |> User.password_reset_changeset(%{ + "password" => password, + "reset_password_sent_at" => nil, + "reset_password_token" => nil + }) + |> Repo.update() - _err -> - {:error, - gettext( - "The token you provided is invalid. Make sure that the URL is exactly the one provided inside the email you got." - )} + nil -> + {:error, :user_not_found} end end diff --git a/test/graphql/resolvers/comment_test.exs b/test/graphql/resolvers/comment_test.exs index cbec5a30c..9a26d766c 100644 --- a/test/graphql/resolvers/comment_test.exs +++ b/test/graphql/resolvers/comment_test.exs @@ -170,7 +170,7 @@ defmodule Mobilizon.GraphQL.Resolvers.CommentTest do # Change the current actor for user actor2 = insert(:actor, user: user) - Mobilizon.Users.update_user_default_actor(user.id, actor2.id) + Mobilizon.Users.update_user_default_actor(user.id, actor2) res = conn @@ -183,7 +183,7 @@ defmodule Mobilizon.GraphQL.Resolvers.CommentTest do assert hd(res["errors"])["message"] == "You cannot delete this comment" - Mobilizon.Users.update_user_default_actor(user.id, actor.id) + Mobilizon.Users.update_user_default_actor(user.id, actor) res = conn diff --git a/test/graphql/resolvers/discussion_test.exs b/test/graphql/resolvers/discussion_test.exs index e0adc56ec..5b5b2cece 100644 --- a/test/graphql/resolvers/discussion_test.exs +++ b/test/graphql/resolvers/discussion_test.exs @@ -448,7 +448,7 @@ defmodule Mobilizon.GraphQL.Resolvers.DiscussionTest do # # Change the current actor for user # actor2 = insert(:actor, user: user) - # Mobilizon.Users.update_user_default_actor(user.id, actor2.id) + # Mobilizon.Users.update_user_default_actor(user.id, actor2) # res = # conn @@ -461,7 +461,7 @@ defmodule Mobilizon.GraphQL.Resolvers.DiscussionTest do # assert hd(res["errors"])["message"] == # "You cannot delete this comment" - # Mobilizon.Users.update_user_default_actor(user.id, actor.id) + # Mobilizon.Users.update_user_default_actor(user.id, actor) # res = # conn diff --git a/test/graphql/resolvers/event_test.exs b/test/graphql/resolvers/event_test.exs index 1d8423c62..b426ea8ea 100644 --- a/test/graphql/resolvers/event_test.exs +++ b/test/graphql/resolvers/event_test.exs @@ -1225,7 +1225,7 @@ defmodule Mobilizon.Web.Resolvers.EventTest do %Actor{} = administrator_actor = insert(:actor, user: user) insert(:member, parent: group, actor: administrator_actor, role: :administrator) - %Actor{id: not_member_actor_id} = insert(:actor, user: user) + %Actor{id: not_member_actor_id} = not_member_actor = insert(:actor, user: user) %Event{} = event = insert(:event, attributed_to: group, organizer_actor: administrator_actor) @@ -1235,7 +1235,7 @@ defmodule Mobilizon.Web.Resolvers.EventTest do |> Map.put(:attributed_to_id, "#{group_id}") |> Map.put(:eventId, to_string(event.id)) - Users.update_user_default_actor(user.id, member_not_approved_actor_id) + Users.update_user_default_actor(user.id, member_not_approved_actor) res = conn @@ -1250,7 +1250,7 @@ defmodule Mobilizon.Web.Resolvers.EventTest do assert hd(res["errors"])["message"] == "This profile doesn't have permission to update an event on behalf of this group" - Users.update_user_default_actor(user.id, not_member_actor_id) + Users.update_user_default_actor(user.id, not_member_actor) res = conn @@ -1265,7 +1265,7 @@ defmodule Mobilizon.Web.Resolvers.EventTest do assert hd(res["errors"])["message"] == "This profile doesn't have permission to update an event on behalf of this group" - Users.update_user_default_actor(user.id, member_actor_id) + Users.update_user_default_actor(user.id, member_actor) res = conn @@ -1280,7 +1280,7 @@ defmodule Mobilizon.Web.Resolvers.EventTest do assert hd(res["errors"])["message"] == "This profile doesn't have permission to update an event on behalf of this group" - Users.update_user_default_actor(user.id, moderator_actor_id) + Users.update_user_default_actor(user.id, moderator_actor) res = conn diff --git a/test/graphql/resolvers/member_test.exs b/test/graphql/resolvers/member_test.exs index 3ea20452a..db2a42f08 100644 --- a/test/graphql/resolvers/member_test.exs +++ b/test/graphql/resolvers/member_test.exs @@ -423,7 +423,7 @@ defmodule Mobilizon.GraphQL.Resolvers.MemberTest do } do user = insert(:user) actor = insert(:actor, user: user) - Mobilizon.Users.update_user_default_actor(user.id, actor.id) + Mobilizon.Users.update_user_default_actor(user.id, actor) %Member{id: member_id} = insert(:member, %{actor: target_actor, parent: group, role: :member}) @@ -449,7 +449,7 @@ defmodule Mobilizon.GraphQL.Resolvers.MemberTest do group: group, target_actor: target_actor } do - Mobilizon.Users.update_user_default_actor(user.id, actor.id) + Mobilizon.Users.update_user_default_actor(user.id, actor) insert(:member, actor: actor, parent: group, role: :administrator) %Member{id: member_id} = @@ -504,7 +504,7 @@ defmodule Mobilizon.GraphQL.Resolvers.MemberTest do actor: actor, group: group } do - Mobilizon.Users.update_user_default_actor(user.id, actor.id) + Mobilizon.Users.update_user_default_actor(user.id, actor) %Member{id: member_id} = insert(:member, actor: actor, parent: group, role: :administrator) res = diff --git a/test/graphql/resolvers/user_test.exs b/test/graphql/resolvers/user_test.exs index c418dbeb1..906cfa03e 100644 --- a/test/graphql/resolvers/user_test.exs +++ b/test/graphql/resolvers/user_test.exs @@ -873,6 +873,7 @@ defmodule Mobilizon.GraphQL.Resolvers.UserTest do test "test refresh_token/3 with an appropriate token", context do user = insert(:user) + insert(:actor, user: user) {:ok, refresh_token} = Authenticator.generate_refresh_token(user) mutation = """ diff --git a/test/service/actor_suspension_test.exs b/test/service/actor_suspension_test.exs index 6c5f0d203..5932e83c2 100644 --- a/test/service/actor_suspension_test.exs +++ b/test/service/actor_suspension_test.exs @@ -12,7 +12,9 @@ defmodule Mobilizon.Service.ActorSuspensionTest do describe "suspend a person" do setup do - %Actor{} = actor = insert(:actor) + user = insert(:user) + %Actor{} = actor = insert(:actor, user: user) + insert(:actor, user: user) %Comment{} = comment = insert(:comment, actor: actor) %Event{} = event = insert(:event, organizer_actor: actor)