From 9430f1145f8f10dec428e85b0b6982c7c18fe3a4 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 9 Oct 2020 15:26:37 +0200 Subject: [PATCH] Fix profiles not administrators able to edit a group Related to #385 Signed-off-by: Thomas Citharel --- js/src/mixins/group.ts | 49 ++++++++++++ js/src/views/Group/Group.vue | 55 ++------------ js/src/views/Group/GroupMembers.vue | 39 +++------- js/src/views/Group/GroupSettings.vue | 30 +++----- js/src/views/Group/Settings.vue | 9 +-- lib/graphql/resolvers/group.ex | 10 ++- lib/mobilizon/actors/actors.ex | 16 ++++ test/graphql/resolvers/group_test.exs | 105 ++++++++++++++++++++++++++ 8 files changed, 204 insertions(+), 109 deletions(-) create mode 100644 js/src/mixins/group.ts diff --git a/js/src/mixins/group.ts b/js/src/mixins/group.ts new file mode 100644 index 000000000..225e5f374 --- /dev/null +++ b/js/src/mixins/group.ts @@ -0,0 +1,49 @@ +import { PERSON_MEMBERSHIPS, CURRENT_ACTOR_CLIENT } from "@/graphql/actor"; +import { FETCH_GROUP } from "@/graphql/group"; +import { Group, IActor, IGroup, IPerson, MemberRole } from "@/types/actor"; +import { Component, Vue } from "vue-property-decorator"; + +@Component({ + apollo: { + group: { + query: FETCH_GROUP, + fetchPolicy: "cache-and-network", + variables() { + return { + name: this.$route.params.preferredUsername, + }; + }, + skip() { + return !this.$route.params.preferredUsername; + }, + }, + person: { + query: PERSON_MEMBERSHIPS, + fetchPolicy: "cache-and-network", + variables() { + return { + id: this.currentActor.id, + }; + }, + skip() { + return !this.currentActor || !this.currentActor.id; + }, + }, + currentActor: CURRENT_ACTOR_CLIENT, + }, +}) +export default class GroupMixin extends Vue { + group: IGroup = new Group(); + currentActor!: IActor; + + person!: IPerson; + + get isCurrentActorAGroupAdmin(): boolean { + return ( + this.person && + this.person.memberships.elements.some( + ({ parent: { id }, role }) => id === this.group.id && role === MemberRole.ADMINISTRATOR + ) + ); + } +} diff --git a/js/src/views/Group/Group.vue b/js/src/views/Group/Group.vue index 093bb781b..d0b811d29 100644 --- a/js/src/views/Group/Group.vue +++ b/js/src/views/Group/Group.vue @@ -338,19 +338,9 @@ diff --git a/lib/graphql/resolvers/group.ex b/lib/graphql/resolvers/group.ex index 01b581f8a..c33c967af 100644 --- a/lib/graphql/resolvers/group.ex +++ b/lib/graphql/resolvers/group.ex @@ -145,11 +145,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do end @doc """ - Create a new group. The creator is automatically added as admin + Update a group. The creator is automatically added as admin """ def update_group( _parent, - args, + %{id: group_id} = args, %{ context: %{ current_user: %User{} = user @@ -157,6 +157,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do } ) do with %Actor{} = updater_actor <- Users.get_actor_for_user(user), + {:administrator, true} <- + {:administrator, Actors.is_administrator?(updater_actor.id, group_id)}, args <- Map.put(args, :updater_actor, updater_actor), args <- save_attached_pictures(args), {:ok, _activity, %Actor{type: :Group} = group} <- @@ -166,8 +168,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do {:error, err} when is_binary(err) -> {:error, err} - {:is_owned, nil} -> - {:error, dgettext("errors", "Creator profile is not owned by the current user")} + {:administrator, false} -> + {:error, dgettext("errors", "Profile is not administrator for the group")} end end diff --git a/lib/mobilizon/actors/actors.ex b/lib/mobilizon/actors/actors.ex index c78f4c582..d6ee2da76 100644 --- a/lib/mobilizon/actors/actors.ex +++ b/lib/mobilizon/actors/actors.ex @@ -704,6 +704,22 @@ defmodule Mobilizon.Actors do ) end + @spec is_moderator?(integer | String.t(), integer | String.t()) :: boolean() + def is_moderator?(actor_id, parent_id) do + match?( + {:ok, %Member{}}, + get_member(actor_id, parent_id, @moderator_roles) + ) + end + + @spec is_administrator?(integer | String.t(), integer | String.t()) :: boolean() + def is_administrator?(actor_id, parent_id) do + match?( + {:ok, %Member{}}, + get_member(actor_id, parent_id, @administrator_roles) + ) + end + @doc """ Gets a single member of an actor (for example a group). """ diff --git a/test/graphql/resolvers/group_test.exs b/test/graphql/resolvers/group_test.exs index 58296294f..cdcabfe60 100644 --- a/test/graphql/resolvers/group_test.exs +++ b/test/graphql/resolvers/group_test.exs @@ -239,6 +239,111 @@ defmodule Mobilizon.Web.Resolvers.GroupTest do end end + describe "update a group" do + @update_group_mutation """ + mutation UpdateGroup( + $id: ID! + $name: String + $summary: String + $avatar: PictureInput + $banner: PictureInput + $visibility: GroupVisibility + $physicalAddress: AddressInput + ) { + updateGroup( + id: $id + name: $name + summary: $summary + banner: $banner + avatar: $avatar + visibility: $visibility + physicalAddress: $physicalAddress + ) { + id + preferredUsername + name + summary + visibility + avatar { + url + } + banner { + url + } + } + } + """ + @new_group_name "new name for group" + + test "update_group/3 updates a group", %{conn: conn, user: user, actor: actor} do + group = insert(:group) + insert(:member, parent: group, actor: actor, role: :administrator) + + res = + conn + |> auth_conn(user) + |> AbsintheHelpers.graphql_query( + query: @update_group_mutation, + variables: %{ + id: group.id, + name: @new_group_name, + visibility: "UNLISTED" + } + ) + + assert is_nil(res["errors"]) + assert res["data"]["updateGroup"]["name"] == @new_group_name + assert res["data"]["updateGroup"]["visibility"] == "UNLISTED" + end + + test "update_group/3 requires to be logged-in to update a group", %{conn: conn} do + group = insert(:group) + + res = + conn + |> AbsintheHelpers.graphql_query( + query: @update_group_mutation, + variables: %{id: group.id, name: @new_group_name} + ) + + assert hd(res["errors"])["message"] == "You need to be logged-in to update a group" + end + + test "update_group/3 requires to be an admin of the group to update a group", %{ + conn: conn, + actor: actor + } do + group = insert(:group) + insert(:member, parent: group, actor: actor, role: :administrator) + user = insert(:user) + actor2 = insert(:actor, user: user) + + # Actor not member + res = + conn + |> auth_conn(user) + |> AbsintheHelpers.graphql_query( + query: @update_group_mutation, + variables: %{id: group.id, name: @new_group_name} + ) + + assert hd(res["errors"])["message"] == "Profile is not administrator for the group" + + # Actor member but not admin + insert(:member, parent: group, actor: actor2, role: :moderator) + + res = + conn + |> auth_conn(user) + |> AbsintheHelpers.graphql_query( + query: @update_group_mutation, + variables: %{id: group.id, name: @new_group_name} + ) + + assert hd(res["errors"])["message"] == "Profile is not administrator for the group" + end + end + describe "delete a group" do @delete_group_mutation """ mutation DeleteGroup($groupId: ID!) {