From dac47d2abbac9f22ec39f29ea9bbb6a31425b764 Mon Sep 17 00:00:00 2001 From: Thomas Citharel <tcit@tcit.fr> Date: Tue, 9 Jun 2020 14:07:49 +0200 Subject: [PATCH] Add config option to allow anonymous reporting Signed-off-by: Thomas Citharel <tcit@tcit.fr> --- CHANGELOG.md | 1 + config/config.exs | 3 + config/dev.exs | 5 + js/src/graphql/comment.ts | 2 +- js/src/graphql/config.ts | 3 + js/src/types/config.model.ts | 3 + js/src/views/Event/Event.vue | 21 +++- js/src/views/Home.vue | 2 +- lib/graphql/resolvers/config.ex | 3 + lib/graphql/resolvers/event.ex | 5 +- lib/graphql/resolvers/report.ex | 28 ++++- lib/graphql/schema/config.ex | 5 + lib/mobilizon/config.ex | 4 + test/graphql/resolvers/report_test.exs | 139 ++++++++++++++++++------- 14 files changed, 177 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c162f3f3..3431a8e8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Also make sure to remove the `EnvironmentFile=` line from the systemd service an - Duplicate an event - Ability to handle basic administration settings in the admin panel - Added physical address change to the list of important changes that trigger event notifications +- Config option to allow anonymous reporting ### Changed - Configuration handling (see above) diff --git a/config/config.exs b/config/config.exs index 5ee17d3fa..012953b09 100644 --- a/config/config.exs +++ b/config/config.exs @@ -202,6 +202,9 @@ config :mobilizon, :anonymous, ], captcha: [enabled: false] } + ], + reports: [ + allowed: false ] config :mobilizon, Oban, diff --git a/config/dev.exs b/config/dev.exs index bfddae9ca..a58b0c969 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -92,6 +92,11 @@ config :mobilizon, :instance, # config :mobilizon, :activitypub, sign_object_fetches: false +config :mobilizon, :anonymous, + reports: [ + allowed: true + ] + require Logger cond do diff --git a/js/src/graphql/comment.ts b/js/src/graphql/comment.ts index 329b66057..8447c2f1f 100644 --- a/js/src/graphql/comment.ts +++ b/js/src/graphql/comment.ts @@ -62,7 +62,7 @@ export const COMMENTS_THREADS = gql` } } } - ${COMMENT_RECURSIVE_FRAGMENT} + ${COMMENT_FIELDS_FRAGMENT} `; export const CREATE_COMMENT_FROM_EVENT = gql` diff --git a/js/src/graphql/config.ts b/js/src/graphql/config.ts index f7d07cbf3..d3a29b436 100644 --- a/js/src/graphql/config.ts +++ b/js/src/graphql/config.ts @@ -34,6 +34,9 @@ export const CONFIG = gql` } } } + reports { + allowed + } actorId } location { diff --git a/js/src/types/config.model.ts b/js/src/types/config.model.ts index 07f83f5c2..9c40936c4 100644 --- a/js/src/types/config.model.ts +++ b/js/src/types/config.model.ts @@ -39,6 +39,9 @@ export interface IConfig { }; }; }; + reports: { + allowed: boolean; + }; actorId: string; }; maps: { diff --git a/js/src/views/Event/Event.vue b/js/src/views/Event/Event.vue index fcd64296d..348ead9c1 100644 --- a/js/src/views/Event/Event.vue +++ b/js/src/views/Event/Event.vue @@ -272,7 +272,7 @@ <b-icon icon="calendar-plus" /> </span> </b-dropdown-item> - <b-dropdown-item aria-role="listitem"> + <b-dropdown-item aria-role="listitem" v-if="ableToReport"> <span @click="isReportModalActive = true"> {{ $t("Report") }} <b-icon icon="flag" /> @@ -750,14 +750,25 @@ export default class Event extends EventMixin { async reportEvent(content: string, forward: boolean) { this.isReportModalActive = false; + // @ts-ignore + this.$refs.reportModal.close(); if (!this.event.organizerActor) return; const eventTitle = this.event.title; + let reporterId = null; + if (this.currentActor.id) { + reporterId = this.currentActor.id; + } else { + if (this.config.anonymous.reports.allowed) { + reporterId = this.config.anonymous.actorId; + } + } + if (!reporterId) return; try { await this.$apollo.mutate<IReport>({ mutation: CREATE_REPORT, variables: { eventId: this.event.id, - reporterId: this.currentActor.id, + reporterId, reportedId: this.event.organizerActor.id, content, forward, @@ -996,6 +1007,12 @@ export default class Event extends EventMixin { await removeAnonymousParticipation(this.uuid); this.anonymousParticipation = null; } + + get ableToReport(): boolean { + return ( + this.config && (this.currentActor.id != undefined || this.config.anonymous.reports.allowed) + ); + } } </script> <style lang="scss" scoped> diff --git a/js/src/views/Home.vue b/js/src/views/Home.vue index 23f4c83d2..5bf342cc8 100644 --- a/js/src/views/Home.vue +++ b/js/src/views/Home.vue @@ -154,7 +154,7 @@ import Subtitle from "../components/Utils/Subtitle.vue"; }; }, update: (data) => - data.loggedUser.participations.map( + data.loggedUser.participations.elements.map( (participation: IParticipant) => new Participant(participation) ), skip() { diff --git a/lib/graphql/resolvers/config.ex b/lib/graphql/resolvers/config.ex index 46f16ab82..0c8a45b24 100644 --- a/lib/graphql/resolvers/config.ex +++ b/lib/graphql/resolvers/config.ex @@ -89,6 +89,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Config do } } }, + reports: %{ + allowed: Config.anonymous_reporting?() + }, actor_id: Config.anonymous_actor_id() }, geocoding: %{ diff --git a/lib/graphql/resolvers/event.ex b/lib/graphql/resolvers/event.ex index 12b1eb4aa..342b3d6b7 100644 --- a/lib/graphql/resolvers/event.ex +++ b/lib/graphql/resolvers/event.ex @@ -60,9 +60,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do end end - def check_event_access(%Event{local: true}), do: true + @spec check_event_access(Event.t()) :: boolean() + defp check_event_access(%Event{local: true}), do: true - def check_event_access(%Event{url: url}) do + defp check_event_access(%Event{url: url}) do relay_actor_id = Config.relay_actor_id() Events.check_if_event_has_instance_follow(url, relay_actor_id) end diff --git a/lib/graphql/resolvers/report.ex b/lib/graphql/resolvers/report.ex index 6e07b10bf..0d8e19627 100644 --- a/lib/graphql/resolvers/report.ex +++ b/lib/graphql/resolvers/report.ex @@ -7,6 +7,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Report do alias Mobilizon.Actors alias Mobilizon.Actors.Actor + alias Mobilizon.Config alias Mobilizon.Reports alias Mobilizon.Reports.{Note, Report} alias Mobilizon.Users.User @@ -47,7 +48,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Report do def create_report( _parent, %{reporter_id: reporter_id} = args, - %{context: %{current_user: user}} = _resolution + %{context: %{current_user: %User{} = user}} = _resolution ) do with {:is_owned, %Actor{}} <- User.owns_actor(user, reporter_id), {:ok, _, %Report{} = report} <- API.Reports.report(args) do @@ -61,6 +62,31 @@ defmodule Mobilizon.GraphQL.Resolvers.Report do end end + @doc """ + Create a report anonymously if allowed + """ + def create_report( + _parent, + %{reporter_id: reporter_id} = args, + _resolution + ) do + with {:anonymous_reporting_allowed, true} <- + {:anonymous_reporting_allowed, Config.anonymous_reporting?()}, + {:wrong_id, true} <- {:wrong_id, reporter_id == to_string(Config.anonymous_actor_id())}, + {:ok, _, %Report{} = report} <- API.Reports.report(args) do + {:ok, report} + else + {:anonymous_reporting_allowed, _} -> + {:error, "You need to be logged-in to create reports"} + + {:wrong_id, _} -> + {:error, "Reporter ID is not the anonymous actor id"} + + _error -> + {:error, "Error while saving report"} + end + end + def create_report(_parent, _args, _resolution) do {:error, "You need to be logged-in to create reports"} end diff --git a/lib/graphql/schema/config.ex b/lib/graphql/schema/config.ex index f67ddb616..0eee6cd9c 100644 --- a/lib/graphql/schema/config.ex +++ b/lib/graphql/schema/config.ex @@ -59,6 +59,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do object :anonymous do field(:participation, :anonymous_participation) field(:event_creation, :anonymous_event_creation) + field(:reports, :anonymous_reports) field(:actor_id, :id) end @@ -100,6 +101,10 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do field(:enabled, :boolean) end + object :anonymous_reports do + field(:allowed, :boolean) + end + object :resource_provider do field(:type, :string) field(:endpoint, :string) diff --git a/lib/mobilizon/config.ex b/lib/mobilizon/config.ex index 14f6de390..8fdd79526 100644 --- a/lib/mobilizon/config.ex +++ b/lib/mobilizon/config.ex @@ -139,6 +139,10 @@ defmodule Mobilizon.Config do :enabled ] + @spec anonymous_reporting? :: boolean + def anonymous_reporting?, + do: Application.get_env(:mobilizon, :anonymous)[:reports][:allowed] + def instance_resource_providers do types = get_in(Application.get_env(:mobilizon, Mobilizon.Service.ResourceProviders), [:types]) diff --git a/test/graphql/resolvers/report_test.exs b/test/graphql/resolvers/report_test.exs index 484cae0e9..7f7e209ec 100644 --- a/test/graphql/resolvers/report_test.exs +++ b/test/graphql/resolvers/report_test.exs @@ -1,9 +1,11 @@ defmodule Mobilizon.GraphQL.Resolvers.ReportTest do use Mobilizon.Web.ConnCase + use Mobilizon.Tests.Helpers import Mobilizon.Factory alias Mobilizon.Actors.Actor + alias Mobilizon.Config alias Mobilizon.Events.Event alias Mobilizon.Reports.{Note, Report} alias Mobilizon.Users.User @@ -11,67 +13,124 @@ defmodule Mobilizon.GraphQL.Resolvers.ReportTest do alias Mobilizon.GraphQL.AbsintheHelpers describe "Resolver: Report a content" do + @create_report_mutation """ + mutation CreateReport($reporterId: ID!, $reportedId: ID!, $eventId: ID, $content: String) { + createReport( + reporterId: $reporterId, + reportedId: $reportedId, + eventId: $eventId, + content: $content + ) { + content, + reporter { + id + }, + event { + id + }, + status + } + } + """ + + clear_config([:anonymous, :reports]) + + setup %{conn: conn} do + Mobilizon.Config.clear_config_cache() + anonymous_actor_id = Config.anonymous_actor_id() + {:ok, conn: conn, anonymous_actor_id: anonymous_actor_id} + end + test "create_report/3 creates a report", %{conn: conn} do %User{} = user_reporter = insert(:user) %Actor{} = reporter = insert(:actor, user: user_reporter) %Actor{} = reported = insert(:actor) %Event{} = event = insert(:event, organizer_actor: reported) - mutation = """ - mutation { - createReport( - reporter_id: #{reporter.id}, - reported_id: #{reported.id}, - event_id: #{event.id}, - content: "This is an issue" - ) { - content, - reporter { - id - }, - event { - id - }, - status - } - } - """ - res = conn |> auth_conn(user_reporter) - |> post("/api", AbsintheHelpers.mutation_skeleton(mutation)) + |> AbsintheHelpers.graphql_query( + query: @create_report_mutation, + variables: %{ + reportedId: reported.id, + reporterId: reporter.id, + eventId: event.id, + content: "This is an issue" + } + ) - assert json_response(res, 200)["errors"] == nil - assert json_response(res, 200)["data"]["createReport"]["content"] == "This is an issue" - assert json_response(res, 200)["data"]["createReport"]["status"] == "OPEN" - assert json_response(res, 200)["data"]["createReport"]["event"]["id"] == to_string(event.id) + assert res["errors"] == nil + assert res["data"]["createReport"]["content"] == "This is an issue" + assert res["data"]["createReport"]["status"] == "OPEN" + assert res["data"]["createReport"]["event"]["id"] == to_string(event.id) - assert json_response(res, 200)["data"]["createReport"]["reporter"]["id"] == + assert res["data"]["createReport"]["reporter"]["id"] == to_string(reporter.id) end test "create_report/3 without being connected doesn't create any report", %{conn: conn} do %Actor{} = reported = insert(:actor) - mutation = """ - mutation { - createReport( - reported_id: #{reported.id}, - reporter_id: 5, - content: "This is an issue" - ) { - content - } - } - """ + res = + conn + |> AbsintheHelpers.graphql_query( + query: @create_report_mutation, + variables: %{ + reportedId: reported.id, + reporterId: 5, + content: "This is an issue" + } + ) + + assert res["errors"] |> hd |> Map.get("message") == + "You need to be logged-in to create reports" + end + + test "create_report/3 anonymously creates a report if config has allowed", %{ + conn: conn, + anonymous_actor_id: anonymous_actor_id + } do + %Actor{} = reported = insert(:actor) + Config.put([:anonymous, :reports, :allowed], true) res = conn - |> post("/api", AbsintheHelpers.mutation_skeleton(mutation)) + |> AbsintheHelpers.graphql_query( + query: @create_report_mutation, + variables: %{ + reportedId: reported.id, + reporterId: anonymous_actor_id, + content: "This is an issue" + } + ) - assert json_response(res, 200)["errors"] |> hd |> Map.get("message") == - "You need to be logged-in to create reports" + assert is_nil(res["errors"]) + assert res["data"]["createReport"]["content"] == "This is an issue" + assert res["data"]["createReport"]["status"] == "OPEN" + + assert res["data"]["createReport"]["reporter"]["id"] == + to_string(anonymous_actor_id) + end + + test "create_report/3 anonymously doesn't creates a report if the anonymous actor ID is wrong", + %{conn: conn} do + %Actor{} = reported = insert(:actor) + Config.put([:anonymous, :reports, :allowed], true) + + res = + conn + |> AbsintheHelpers.graphql_query( + query: @create_report_mutation, + variables: %{ + reportedId: reported.id, + reporterId: 53, + content: "This is an issue" + } + ) + + assert res["errors"] |> hd |> Map.get("message") == + "Reporter ID is not the anonymous actor id" end end