Fix search exposing events to unlogged users
Closes #892 Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
729a6a7113
commit
3961a2067b
|
@ -9,19 +9,19 @@
|
||||||
>
|
>
|
||||||
<template slot="label">
|
<template slot="label">
|
||||||
{{ actualLabel }}
|
{{ actualLabel }}
|
||||||
|
<span
|
||||||
|
class="is-size-6 has-text-weight-normal"
|
||||||
|
v-if="gettingLocation"
|
||||||
|
>{{ $t("Getting location") }}</span
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<p class="control" v-if="canShowLocateMeButton && !gettingLocation">
|
||||||
<b-button
|
<b-button
|
||||||
v-if="canShowLocateMeButton && !gettingLocation"
|
|
||||||
size="is-small"
|
|
||||||
icon-right="map-marker"
|
icon-right="map-marker"
|
||||||
@click="locateMe"
|
@click="locateMe"
|
||||||
:title="$t('Use my location')"
|
:title="$t('Use my location')"
|
||||||
/>
|
/>
|
||||||
<span
|
</p>
|
||||||
class="is-size-6 has-text-weight-normal"
|
|
||||||
v-else-if="gettingLocation"
|
|
||||||
>{{ $t("Getting location") }}</span
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
<b-autocomplete
|
<b-autocomplete
|
||||||
:data="addressData"
|
:data="addressData"
|
||||||
v-model="queryText"
|
v-model="queryText"
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
field="fullName"
|
field="fullName"
|
||||||
:loading="isFetching"
|
:loading="isFetching"
|
||||||
@typing="fetchAsyncData"
|
@typing="fetchAsyncData"
|
||||||
icon="map-marker"
|
:icon="canShowLocateMeButton ? null : 'map-marker'"
|
||||||
expanded
|
expanded
|
||||||
@select="updateSelected"
|
@select="updateSelected"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
|
@ -71,7 +71,10 @@
|
||||||
:title="$t('Clear address field')"
|
:title="$t('Clear address field')"
|
||||||
/>
|
/>
|
||||||
</b-field>
|
</b-field>
|
||||||
<div class="card" v-if="selected.originId || selected.url">
|
<div
|
||||||
|
class="card"
|
||||||
|
v-if="!hideSelected && (selected.originId || selected.url)"
|
||||||
|
>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<address-info
|
<address-info
|
||||||
:address="selected"
|
:address="selected"
|
||||||
|
@ -119,6 +122,8 @@ export default class FullAddressAutoComplete extends Mixins(
|
||||||
@Prop({ required: false }) userTimezone!: string;
|
@Prop({ required: false }) userTimezone!: string;
|
||||||
@Prop({ required: false, default: false, type: Boolean }) disabled!: boolean;
|
@Prop({ required: false, default: false, type: Boolean }) disabled!: boolean;
|
||||||
@Prop({ required: false, default: false, type: Boolean }) hideMap!: boolean;
|
@Prop({ required: false, default: false, type: Boolean }) hideMap!: boolean;
|
||||||
|
@Prop({ required: false, default: false, type: Boolean })
|
||||||
|
hideSelected!: boolean;
|
||||||
|
|
||||||
addressModalActive = false;
|
addressModalActive = false;
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
ref="aac"
|
ref="aac"
|
||||||
:placeholder="$t('For instance: London')"
|
:placeholder="$t('For instance: London')"
|
||||||
@input="locchange"
|
@input="locchange"
|
||||||
|
:hideMap="true"
|
||||||
|
:hideSelected="true"
|
||||||
/>
|
/>
|
||||||
<b-field
|
<b-field
|
||||||
:label="$t('Radius')"
|
:label="$t('Radius')"
|
||||||
|
@ -144,9 +146,16 @@
|
||||||
</b-pagination>
|
</b-pagination>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<b-message v-else-if="$apollo.loading === false" type="is-danger">{{
|
<b-message v-else-if="$apollo.loading === false" type="is-danger">
|
||||||
$t("No events found")
|
<p>{{ $t("No events found") }}</p>
|
||||||
}}</b-message>
|
<p v-if="searchIsUrl && !currentUser.id">
|
||||||
|
{{
|
||||||
|
$t(
|
||||||
|
"Only registered users may fetch remote events from their URL."
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
</b-message>
|
||||||
</b-tab-item>
|
</b-tab-item>
|
||||||
<b-tab-item v-if="!tag">
|
<b-tab-item v-if="!tag">
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
|
@ -211,6 +220,8 @@ import MultiGroupCard from "../components/Group/MultiGroupCard.vue";
|
||||||
import { CONFIG } from "../graphql/config";
|
import { CONFIG } from "../graphql/config";
|
||||||
import { REVERSE_GEOCODE } from "../graphql/address";
|
import { REVERSE_GEOCODE } from "../graphql/address";
|
||||||
import debounce from "lodash/debounce";
|
import debounce from "lodash/debounce";
|
||||||
|
import { CURRENT_USER_CLIENT } from "@/graphql/user";
|
||||||
|
import { ICurrentUser } from "@/types/current-user.model";
|
||||||
|
|
||||||
interface ISearchTimeOption {
|
interface ISearchTimeOption {
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -267,6 +278,7 @@ const GEOHASH_DEPTH = 9; // put enough accuracy, radius will be used anyway
|
||||||
this.searchGroups = data.searchGroups;
|
this.searchGroups = data.searchGroups;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
currentUser: CURRENT_USER_CLIENT,
|
||||||
},
|
},
|
||||||
metaInfo() {
|
metaInfo() {
|
||||||
return {
|
return {
|
||||||
|
@ -292,6 +304,8 @@ export default class Search extends Vue {
|
||||||
|
|
||||||
location: IAddress = new Address();
|
location: IAddress = new Address();
|
||||||
|
|
||||||
|
currentUser!: ICurrentUser;
|
||||||
|
|
||||||
dateOptions: Record<string, ISearchTimeOption> = {
|
dateOptions: Record<string, ISearchTimeOption> = {
|
||||||
past: {
|
past: {
|
||||||
label: this.$t("In the past") as string,
|
label: this.$t("In the past") as string,
|
||||||
|
@ -570,6 +584,18 @@ export default class Search extends Vue {
|
||||||
private stringExists(value: string | null | undefined): boolean {
|
private stringExists(value: string | null | undefined): boolean {
|
||||||
return this.valueExists(value) && (value as string).length > 0;
|
return this.valueExists(value) && (value as string).length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get searchIsUrl(): boolean {
|
||||||
|
let url;
|
||||||
|
if (!this.search) return false;
|
||||||
|
try {
|
||||||
|
url = new URL(this.search);
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.protocol === "http:" || url.protocol === "https:";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ defmodule Mobilizon.GraphQL.API.Search do
|
||||||
|
|
||||||
alias Mobilizon.Federation.ActivityPub
|
alias Mobilizon.Federation.ActivityPub
|
||||||
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
|
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
|
||||||
|
import Mobilizon.GraphQL.Resolvers.Event.Utils
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@ -67,10 +68,14 @@ defmodule Mobilizon.GraphQL.API.Search do
|
||||||
term = String.trim(term)
|
term = String.trim(term)
|
||||||
|
|
||||||
if is_url(term) do
|
if is_url(term) do
|
||||||
# skip, if it's w not an actor
|
# skip, if it's not an event
|
||||||
case process_from_url(term) do
|
case process_from_url(term) do
|
||||||
%Page{total: _total, elements: [%Event{} = _event]} = page ->
|
%Page{total: _total, elements: [%Event{} = event]} = page ->
|
||||||
|
if Map.get(args, :current_user) != nil || check_event_access?(event) do
|
||||||
{:ok, page}
|
{:ok, page}
|
||||||
|
else
|
||||||
|
{:ok, %{total: 0, elements: []}}
|
||||||
|
end
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
{:ok, %{total: 0, elements: []}}
|
{:ok, %{total: 0, elements: []}}
|
||||||
|
|
|
@ -117,26 +117,17 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do
|
||||||
@spec find_event(any(), map(), Absinthe.Resolution.t()) ::
|
@spec find_event(any(), map(), Absinthe.Resolution.t()) ::
|
||||||
{:ok, Event.t()} | {:error, :event_not_found}
|
{:ok, Event.t()} | {:error, :event_not_found}
|
||||||
def find_event(parent, %{uuid: uuid} = args, %{context: context} = resolution) do
|
def find_event(parent, %{uuid: uuid} = args, %{context: context} = resolution) do
|
||||||
with {:has_event, %Event{} = event} <-
|
case Events.get_public_event_by_uuid_with_preload(uuid) do
|
||||||
{:has_event, Events.get_public_event_by_uuid_with_preload(uuid)},
|
%Event{} = event ->
|
||||||
{:access_valid, true} <-
|
if Map.has_key?(context, :current_user) || check_event_access?(event) do
|
||||||
{:access_valid, Map.has_key?(context, :current_user) || check_event_access(event)} do
|
|
||||||
{:ok, event}
|
{:ok, event}
|
||||||
else
|
else
|
||||||
{:has_event, _} ->
|
|
||||||
find_private_event(parent, args, resolution)
|
|
||||||
|
|
||||||
{:access_valid, _} ->
|
|
||||||
{:error, :event_not_found}
|
{:error, :event_not_found}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
find_private_event(parent, args, resolution)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec check_event_access(Event.t()) :: boolean()
|
|
||||||
defp check_event_access(%Event{local: true}), do: true
|
|
||||||
|
|
||||||
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
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
@ -4,6 +4,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Event.Utils do
|
||||||
"""
|
"""
|
||||||
|
|
||||||
alias Mobilizon.Actors.Actor
|
alias Mobilizon.Actors.Actor
|
||||||
|
alias Mobilizon.{Config, Events}
|
||||||
alias Mobilizon.Events.Event
|
alias Mobilizon.Events.Event
|
||||||
alias Mobilizon.Federation.ActivityPub.Permission
|
alias Mobilizon.Federation.ActivityPub.Permission
|
||||||
import Mobilizon.Service.Guards, only: [is_valid_string: 1]
|
import Mobilizon.Service.Guards, only: [is_valid_string: 1]
|
||||||
|
@ -37,4 +38,12 @@ defmodule Mobilizon.GraphQL.Resolvers.Event.Utils do
|
||||||
def can_event_be_deleted_by?(%Event{} = event, %Actor{id: actor_member_id}) do
|
def can_event_be_deleted_by?(%Event{} = event, %Actor{id: actor_member_id}) do
|
||||||
Event.can_be_managed_by?(event, actor_member_id)
|
Event.can_be_managed_by?(event, actor_member_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec check_event_access?(Event.t()) :: boolean()
|
||||||
|
def check_event_access?(%Event{local: true}), do: true
|
||||||
|
|
||||||
|
def 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
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,7 +26,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Search do
|
||||||
%{page: page, limit: limit} = args,
|
%{page: page, limit: limit} = args,
|
||||||
%{context: context} = _resolution
|
%{context: context} = _resolution
|
||||||
) do
|
) do
|
||||||
current_actor = Map.get(context, :current_actor, nil)
|
current_actor = Map.get(context, :current_actor)
|
||||||
current_actor_id = if current_actor, do: current_actor.id, else: nil
|
current_actor_id = if current_actor, do: current_actor.id, else: nil
|
||||||
args = Map.put(args, :current_actor_id, current_actor_id)
|
args = Map.put(args, :current_actor_id, current_actor_id)
|
||||||
Search.search_actors(args, page, limit, :Group)
|
Search.search_actors(args, page, limit, :Group)
|
||||||
|
@ -37,7 +37,13 @@ defmodule Mobilizon.GraphQL.Resolvers.Search do
|
||||||
"""
|
"""
|
||||||
@spec search_events(any(), map(), Absinthe.Resolution.t()) ::
|
@spec search_events(any(), map(), Absinthe.Resolution.t()) ::
|
||||||
{:ok, Page.t(Event.t())} | {:error, String.t()}
|
{:ok, Page.t(Event.t())} | {:error, String.t()}
|
||||||
def search_events(_parent, %{page: page, limit: limit} = args, _resolution) do
|
def search_events(
|
||||||
|
_parent,
|
||||||
|
%{page: page, limit: limit} = args,
|
||||||
|
%{context: context} = _resolution
|
||||||
|
) do
|
||||||
|
current_user = Map.get(context, :current_user)
|
||||||
|
args = Map.put(args, :current_user, current_user)
|
||||||
Search.search_events(args, page, limit)
|
Search.search_events(args, page, limit)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue