forked from potsda.mn/mobilizon
Merge branch 'usage-restrictions' into 'master'
add "only platform admin can create groups" and "only groups can create events" restrictions See merge request framasoft/mobilizon!1078
This commit is contained in:
commit
db55859855
|
@ -40,9 +40,11 @@ config :mobilizon, :instance,
|
|||
email_reply_to: "noreply@localhost"
|
||||
|
||||
config :mobilizon, :groups, enabled: true
|
||||
|
||||
config :mobilizon, :events, creation: true
|
||||
|
||||
config :mobilizon, :restrictions, only_admin_can_create_groups: false
|
||||
config :mobilizon, :restrictions, only_groups_can_create_events: false
|
||||
|
||||
# Configures the endpoint
|
||||
config :mobilizon, Mobilizon.Web.Endpoint,
|
||||
url: [
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
"
|
||||
>
|
||||
<b-button
|
||||
v-if="!hideCreateEventsButton"
|
||||
tag="router-link"
|
||||
:to="{ name: RouteName.CREATE_EVENT }"
|
||||
type="is-primary"
|
||||
|
@ -313,6 +314,10 @@ export default class NavBar extends Vue {
|
|||
});
|
||||
return changeIdentity(this.$apollo.provider.defaultClient, identity);
|
||||
}
|
||||
|
||||
get hideCreateEventsButton(): boolean {
|
||||
return !!this.config?.restrictions?.onlyGroupsCanCreateEvents;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -69,6 +69,10 @@ export const CONFIG = gql`
|
|||
eventCreation
|
||||
koenaConnect
|
||||
}
|
||||
restrictions {
|
||||
onlyAdminCanCreateGroups
|
||||
onlyGroupsCanCreateEvents
|
||||
}
|
||||
auth {
|
||||
ldap
|
||||
oauthProviders {
|
||||
|
|
|
@ -84,6 +84,10 @@ export interface IConfig {
|
|||
groups: boolean;
|
||||
koenaConnect: boolean;
|
||||
};
|
||||
restrictions: {
|
||||
onlyAdminCanCreateGroups: boolean;
|
||||
onlyGroupsCanCreateEvents: boolean;
|
||||
};
|
||||
federating: boolean;
|
||||
version: string;
|
||||
auth: {
|
||||
|
|
|
@ -14,6 +14,13 @@
|
|||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="buttons" v-if="showCreateGroupsButton">
|
||||
<router-link
|
||||
class="button is-primary"
|
||||
:to="{ name: RouteName.CREATE_GROUP }"
|
||||
>{{ $t("Create group") }}</router-link
|
||||
>
|
||||
</div>
|
||||
<div v-if="groups">
|
||||
<b-switch v-model="local">{{ $t("Local") }}</b-switch>
|
||||
<b-switch v-model="suspended">{{ $t("Suspended") }}</b-switch>
|
||||
|
@ -100,6 +107,8 @@
|
|||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from "vue-property-decorator";
|
||||
import { CONFIG } from "@/graphql/config";
|
||||
import { IConfig } from "@/types/config.model";
|
||||
import { LIST_GROUPS } from "@/graphql/group";
|
||||
import RouteName from "../../router/name";
|
||||
import EmptyContent from "../../components/Utils/EmptyContent.vue";
|
||||
|
@ -110,6 +119,7 @@ const PROFILES_PER_PAGE = 10;
|
|||
|
||||
@Component({
|
||||
apollo: {
|
||||
config: CONFIG,
|
||||
groups: {
|
||||
query: LIST_GROUPS,
|
||||
variables() {
|
||||
|
@ -139,6 +149,7 @@ export default class GroupProfiles extends Vue {
|
|||
|
||||
PROFILES_PER_PAGE = PROFILES_PER_PAGE;
|
||||
|
||||
config!: IConfig;
|
||||
RouteName = RouteName;
|
||||
|
||||
async onPageChange(): Promise<void> {
|
||||
|
@ -185,6 +196,10 @@ export default class GroupProfiles extends Vue {
|
|||
this.pushRouter({ suspended: suspended ? "1" : "0" });
|
||||
}
|
||||
|
||||
get showCreateGroupsButton(): boolean {
|
||||
return !!this.config?.restrictions?.onlyAdminCanCreateGroups;
|
||||
}
|
||||
|
||||
onFiltersChange({
|
||||
preferredUsername,
|
||||
domain,
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
)
|
||||
}}
|
||||
</p>
|
||||
<div class="buttons">
|
||||
<div class="buttons" v-if="!hideCreateEventButton">
|
||||
<router-link
|
||||
class="button is-primary"
|
||||
:to="{ name: RouteName.CREATE_EVENT }"
|
||||
|
@ -126,6 +126,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { CONFIG } from "../../graphql/config";
|
||||
import { IConfig } from "../../types/config.model";
|
||||
import { Component, Vue } from "vue-property-decorator";
|
||||
import { ParticipantRole } from "@/types/enums";
|
||||
import RouteName from "@/router/name";
|
||||
|
@ -147,6 +149,7 @@ import Subtitle from "../../components/Utils/Subtitle.vue";
|
|||
EventListCard,
|
||||
},
|
||||
apollo: {
|
||||
config: CONFIG,
|
||||
futureParticipations: {
|
||||
query: LOGGED_USER_PARTICIPATIONS,
|
||||
fetchPolicy: "cache-and-network",
|
||||
|
@ -197,6 +200,8 @@ export default class MyEvents extends Vue {
|
|||
|
||||
limit = 10;
|
||||
|
||||
config!: IConfig;
|
||||
|
||||
futureParticipations: IParticipant[] = [];
|
||||
|
||||
hasMoreFutureParticipations = true;
|
||||
|
@ -286,6 +291,10 @@ export default class MyEvents extends Vue {
|
|||
(participation) => participation.event.id !== eventid
|
||||
);
|
||||
}
|
||||
|
||||
get hideCreateEventButton(): boolean {
|
||||
return !!this.config?.restrictions?.onlyGroupsCanCreateEvents;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
)
|
||||
}}
|
||||
</p>
|
||||
<div class="buttons">
|
||||
<div class="buttons" v-if="!hideCreateGroupButton">
|
||||
<router-link
|
||||
class="button is-primary"
|
||||
:to="{ name: RouteName.CREATE_GROUP }"
|
||||
|
@ -72,6 +72,8 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from "vue-property-decorator";
|
||||
import { CONFIG } from "@/graphql/config";
|
||||
import { IConfig } from "@/types/config.model";
|
||||
import { LOGGED_USER_MEMBERSHIPS } from "@/graphql/actor";
|
||||
import { LEAVE_GROUP } from "@/graphql/group";
|
||||
import GroupMemberCard from "@/components/Group/GroupMemberCard.vue";
|
||||
|
@ -90,6 +92,9 @@ import RouteName from "../../router/name";
|
|||
Invitations,
|
||||
},
|
||||
apollo: {
|
||||
config: {
|
||||
query: CONFIG,
|
||||
},
|
||||
membershipsPages: {
|
||||
query: LOGGED_USER_MEMBERSHIPS,
|
||||
fetchPolicy: "cache-and-network",
|
||||
|
@ -114,6 +119,8 @@ export default class MyGroups extends Vue {
|
|||
|
||||
RouteName = RouteName;
|
||||
|
||||
config!: IConfig;
|
||||
|
||||
page = 1;
|
||||
|
||||
limit = 10;
|
||||
|
@ -177,6 +184,10 @@ export default class MyGroups extends Vue {
|
|||
![MemberRole.INVITED, MemberRole.REJECTED].includes(member.role)
|
||||
);
|
||||
}
|
||||
|
||||
get hideCreateGroupButton(): boolean {
|
||||
return !!this.config?.restrictions?.onlyAdminCanCreateGroups;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -56,6 +56,11 @@ export const configMock = {
|
|||
groups: true,
|
||||
koenaConnect: false,
|
||||
},
|
||||
restrictions: {
|
||||
__typename: "Restrictions",
|
||||
onlyAdminCanCreateGroups: false,
|
||||
onlyGroupsCanCreateEvents: false,
|
||||
},
|
||||
geocoding: {
|
||||
__typename: "Geocoding",
|
||||
autocomplete: true,
|
||||
|
|
|
@ -134,6 +134,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Config do
|
|||
event_creation: Config.instance_event_creation_enabled?(),
|
||||
koena_connect: Config.get([:instance, :koena_connect_link], false)
|
||||
},
|
||||
restrictions: %{
|
||||
only_admin_can_create_groups: Config.only_admin_can_create_groups?(),
|
||||
only_groups_can_create_events: Config.only_groups_can_create_events?()
|
||||
},
|
||||
rules: Config.instance_rules(),
|
||||
version: Config.instance_version(),
|
||||
federating: Config.instance_federating(),
|
||||
|
|
|
@ -265,29 +265,33 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do
|
|||
%{context: %{current_user: user}} = _resolution
|
||||
) do
|
||||
# See https://github.com/absinthe-graphql/absinthe/issues/490
|
||||
with {:is_owned, %Actor{} = organizer_actor} <- User.owns_actor(user, organizer_actor_id),
|
||||
args <- Map.put(args, :options, args[:options] || %{}),
|
||||
{:group_check, true} <- {:group_check, is_organizer_group_member?(args)},
|
||||
args_with_organizer <- Map.put(args, :organizer_actor, organizer_actor),
|
||||
{:ok, %Activity{data: %{"object" => %{"type" => "Event"}}}, %Event{} = event} <-
|
||||
API.Events.create_event(args_with_organizer) do
|
||||
{:ok, event}
|
||||
if Config.only_groups_can_create_events?() and Map.get(args, :attributed_to_id) == nil do
|
||||
{:error, "only groups can create events"}
|
||||
else
|
||||
{:group_check, false} ->
|
||||
{:error,
|
||||
dgettext(
|
||||
"errors",
|
||||
"Organizer profile doesn't have permission to create an event on behalf of this group"
|
||||
)}
|
||||
with {:is_owned, %Actor{} = organizer_actor} <- User.owns_actor(user, organizer_actor_id),
|
||||
args <- Map.put(args, :options, args[:options] || %{}),
|
||||
{:group_check, true} <- {:group_check, is_organizer_group_member?(args)},
|
||||
args_with_organizer <- Map.put(args, :organizer_actor, organizer_actor),
|
||||
{:ok, %Activity{data: %{"object" => %{"type" => "Event"}}}, %Event{} = event} <-
|
||||
API.Events.create_event(args_with_organizer) do
|
||||
{:ok, event}
|
||||
else
|
||||
{:group_check, false} ->
|
||||
{:error,
|
||||
dgettext(
|
||||
"errors",
|
||||
"Organizer profile doesn't have permission to create an event on behalf of this group"
|
||||
)}
|
||||
|
||||
{:is_owned, nil} ->
|
||||
{:error, dgettext("errors", "Organizer profile is not owned by the user")}
|
||||
{:is_owned, nil} ->
|
||||
{:error, dgettext("errors", "Organizer profile is not owned by the user")}
|
||||
|
||||
{:error, _, %Ecto.Changeset{} = error, _} ->
|
||||
{:error, error}
|
||||
{:error, _, %Ecto.Changeset{} = error, _} ->
|
||||
{:error, error}
|
||||
|
||||
{:error, %Ecto.Changeset{} = error} ->
|
||||
{:error, error}
|
||||
{:error, %Ecto.Changeset{} = error} ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do
|
|||
"""
|
||||
|
||||
import Mobilizon.Users.Guards
|
||||
alias Mobilizon.Config
|
||||
alias Mobilizon.{Actors, Events}
|
||||
alias Mobilizon.Actors.{Actor, Member}
|
||||
alias Mobilizon.Federation.ActivityPub.Actions
|
||||
|
@ -137,23 +138,29 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do
|
|||
args,
|
||||
%{
|
||||
context: %{
|
||||
current_actor: %Actor{id: creator_actor_id} = creator_actor
|
||||
current_actor: %Actor{id: creator_actor_id} = creator_actor,
|
||||
current_user: %User{role: role} = _resolution
|
||||
}
|
||||
}
|
||||
) do
|
||||
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)},
|
||||
{:ok, _activity, %Actor{type: :Group} = group} <-
|
||||
API.Groups.create_group(args) do
|
||||
{:ok, group}
|
||||
if Config.only_admin_can_create_groups?() and not is_admin(role) do
|
||||
{:error, "only admins can create groups"}
|
||||
else
|
||||
{:picture, {:error, :file_too_large}} ->
|
||||
{:error, dgettext("errors", "The provided picture is too heavy")}
|
||||
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)},
|
||||
{:ok, _activity, %Actor{type: :Group} = group} <-
|
||||
API.Groups.create_group(args) do
|
||||
{:ok, group}
|
||||
else
|
||||
{:picture, {:error, :file_too_large}} ->
|
||||
{:error, dgettext("errors", "The provided picture is too heavy")}
|
||||
|
||||
{:error, err} when is_binary(err) ->
|
||||
{:error, err}
|
||||
{:error, err} when is_binary(err) ->
|
||||
{:error, err}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
|||
|
||||
field(:timezones, list_of(:string), description: "The instance's available timezones")
|
||||
field(:features, :features, description: "The instance's features")
|
||||
field(:restrictions, :restrictions, description: "The instance's restrictions")
|
||||
field(:version, :string, description: "The instance's version")
|
||||
field(:federating, :boolean, description: "Whether this instance is federation")
|
||||
|
||||
|
@ -275,6 +276,19 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
|||
field(:koena_connect, :boolean, description: "Activate link to Koena Connect")
|
||||
end
|
||||
|
||||
@desc """
|
||||
The instance's restrictions
|
||||
"""
|
||||
object :restrictions do
|
||||
field(:only_admin_can_create_groups, :boolean,
|
||||
description: "Whether groups creation is allowed only for admin, not for all users"
|
||||
)
|
||||
|
||||
field(:only_groups_can_create_events, :boolean,
|
||||
description: "Whether events creation is allowed only for groups, not for persons"
|
||||
)
|
||||
end
|
||||
|
||||
@desc """
|
||||
The instance's auth configuration
|
||||
"""
|
||||
|
|
|
@ -288,6 +288,9 @@ defmodule Mobilizon.Config do
|
|||
end
|
||||
end
|
||||
|
||||
# config :mobilizon, :groups, enabled: true
|
||||
# config :mobilizon, :events, creation: true
|
||||
|
||||
@spec instance_group_feature_enabled? :: boolean
|
||||
def instance_group_feature_enabled?,
|
||||
do: :mobilizon |> Application.get_env(:groups) |> Keyword.get(:enabled)
|
||||
|
@ -303,6 +306,20 @@ defmodule Mobilizon.Config do
|
|||
}
|
||||
end
|
||||
|
||||
@spec only_admin_can_create_groups? :: boolean
|
||||
def only_admin_can_create_groups?,
|
||||
do:
|
||||
:mobilizon
|
||||
|> Application.get_env(:restrictions)
|
||||
|> Keyword.get(:only_admin_can_create_groups)
|
||||
|
||||
@spec only_groups_can_create_events? :: boolean
|
||||
def only_groups_can_create_events?,
|
||||
do:
|
||||
:mobilizon
|
||||
|> Application.get_env(:restrictions)
|
||||
|> Keyword.get(:only_groups_can_create_events)
|
||||
|
||||
@spec anonymous_actor_id :: integer
|
||||
def anonymous_actor_id, do: get_cached_value(:anonymous_actor_id)
|
||||
@spec relay_actor_id :: integer
|
||||
|
|
Loading…
Reference in a new issue