Introduce authorizations with Rajska

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2023-03-17 18:10:59 +01:00
parent b6875f6a4b
commit 8984bd7636
95 changed files with 4560 additions and 1505 deletions

View File

@@ -65,18 +65,15 @@
</o-field>
<o-modal
has-modal-card
v-model="showNewElementModal"
v-model:active="showNewElementModal"
:close-button-aria-label="$t('Close')"
>
<div class="modal-card">
<header class="modal-card-head">
<button
type="button"
class="delete"
@click="showNewElementModal = false"
/>
<div class="">
<header class="">
<h2>{{ t('Create a new metadata element') }}</h2>
<p>{{ t('You can put any arbitrary content in this element. URLs will be clickable.') }}</p>
</header>
<div class="modal-card-body">
<div class="">
<form @submit="addNewElement">
<o-field :label="$t('Element title')">
<o-input v-model="newElement.title" />
@@ -84,7 +81,7 @@
<o-field :label="$t('Element value')">
<o-input v-model="newElement.value" />
</o-field>
<o-button variant="primary" native-type="submit">{{
<o-button class="mt-2" variant="primary" native-type="submit">{{
$t("Add")
}}</o-button>
</form>

View File

@@ -70,7 +70,7 @@ function setupApp({ app }) {
new Promise((resolve) =>
resolve({
data: {
identities: [{ id: "9", preferredUsername: "sam", name: "Samuel" }],
loggedUser: { actors: [{ id: "9", preferredUsername: "sam", name: "Samuel" }] },
},
})
)

View File

@@ -1,44 +1,94 @@
<template>
<h1 class="text-3xl">
{{ t("Autorize this application to access your account?") }}
</h1>
<div>
<h1 class="text-3xl">
{{ t("Autorize this application to access your account?") }}
</h1>
<div
class="rounded-lg bg-mbz-warning dark:text-black shadow-xl my-6 p-4 flex items-center gap-2"
>
<AlertCircle :size="42" />
<p>
{{
t(
"This application will be able to access all of your informations and post content on your behalf. Make sure you only approve applications you trust."
)
}}
</p>
</div>
<div class="rounded-lg bg-white dark:bg-zinc-900 shadow-xl my-6">
<div class="p-4 pb-0">
<p class="text-3xl font-bold">{{ authApplication.name }}</p>
<p>{{ authApplication.website }}</p>
</div>
<div class="flex gap-3 p-4">
<o-button @click="() => authorize()">{{ t("Authorize") }}</o-button>
<o-button outlined tag="router-link" :to="{ name: RouteName.HOME }">{{
t("Decline")
}}</o-button>
<div class="rounded-lg bg-white dark:bg-zinc-900 shadow-xl my-6">
<div class="p-4 pb-0">
<p class="text-3xl font-bold">{{ authApplication.name }}</p>
<p>{{ authApplication.website }}</p>
</div>
<p class="p-4">
{{
t(
"You'll be able to revoke access for this application in your account settings."
)
}}
</p>
<div class="">
<div
v-if="collapses.length === 0"
class="rounded-lg bg-mbz-danger shadow-xl my-6 p-4 flex items-center gap-2"
>
<AlertCircle :size="42" />
<p>
{{
t(
"This application didn't ask for known permissions. It's likely the request is incorrect."
)
}}
</p>
</div>
<p v-else class="px-4 font-bold">
{{ t('This application asks for the following permissions:') }}
</p>
<o-collapse
class="mt-3 border-b pb-2 border-zinc-700 text-black dark:text-white"
:class="{
'bg-mbz-warning dark:!text-black': collapse?.type === 'warning',
}"
animation="slide"
v-for="(collapse, index) of collapses"
:key="index"
:open="isOpen === index"
@open="isOpen = index"
>
<template #trigger="props">
<div class="flex py-1" role="button">
<o-icon :icon="collapse.icon" class="px-2" />
<p class="font-bold text-lg p-2 flex-1">
{{ collapse.title }}
</p>
<a
class="flex items-center cursor-pointer p-3 justify-center self-end"
>
<o-icon :icon="props.open ? 'chevron-up' : 'chevron-down'">
</o-icon>
</a>
</div>
</template>
<div class="p-2">
<div class="content">
{{ collapse.text }}
</div>
</div>
</o-collapse>
</div>
<div class="flex gap-3 p-4">
<o-button
:disabled="collapses.length === 0"
@click="() => authorize()"
>{{ t("Authorize") }}</o-button
>
<o-button outlined tag="router-link" :to="{ name: RouteName.HOME }">{{
t("Decline")
}}</o-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { useHead } from "@vueuse/head";
import { computed } from "vue";
import { computed, ref } from "vue";
import { useI18n } from "vue-i18n";
import { useMutation } from "@vue/apollo-composable";
import { AUTORIZE_APPLICATION } from "@/graphql/application";
import AlertCircle from "vue-material-design-icons/AlertCircle.vue";
import RouteName from "@/router/name";
import { IApplication } from "@/types/application.model";
import { scope } from "./scope";
import AlertCircle from "vue-material-design-icons/AlertCircle.vue";
const { t } = useI18n({ useScope: "global" });
@@ -49,9 +99,25 @@ const props = defineProps<{
scope?: string | null;
}>();
const isOpen = ref<number>(-1);
const collapses = computed(() =>
(props.scope ?? "")
.split(" ")
.map((scope) => scope[scope])
.filter((scope) => scope)
);
const { mutate: authorizeMutation, onDone: onAuthorizeMutationDone } =
useMutation<
{ authorizeApplication: { code: string; state: string } },
{
authorizeApplication: {
code: string;
state: string;
clientId: string;
scope: string;
};
},
{
applicationClientId: string;
redirectURI: string;
@@ -71,13 +137,20 @@ const authorize = () => {
onAuthorizeMutationDone(({ data }) => {
const code = data?.authorizeApplication?.code;
const localClientId = data?.authorizeApplication?.clientId;
const localScope = data?.authorizeApplication?.scope;
const returnedState = data?.authorizeApplication?.state ?? "";
if (!code) return;
if (!code || !localClientId || !localScope) return;
if (props.redirectURI) {
const params = new URLSearchParams(
Object.entries({ code, state: returnedState })
Object.entries({
code,
state: returnedState,
client_id: localClientId,
scope: localScope,
})
);
window.location.assign(
new URL(`${props.redirectURI}?${params.toString()}`)

View File

@@ -0,0 +1,283 @@
import { i18n } from "@/utils/i18n";
const t = i18n.global.t;
export const scope: Record<
string,
{ title: string; type?: "warning"; text: string; icon?: string }
> = {
read: {
title: t("Read all of your account's data"),
type: "warning",
text: t(
"This application will be allowed to see all of your events organized, the events you participate to, as well as every data from your groups."
),
icon: "eye-outline",
},
write: {
title: t("Modify all of your account's data"),
text: t(
"This application will be allowed to publish and manage events on your behalf, post and manage comments, participate to events, manage all of your groups, including group events, resources, posts and discussions. It will also be allowed to manage your account and profile settings."
),
type: "warning",
icon: "pencil-outline",
},
"write:event:create": {
title: t("Publish events"),
text: t(
"This application will be allowed to publish events on your behalf"
),
icon: "calendar",
},
"write:event:update": {
title: t("Update events"),
text: t("This application will be allowed to update events on your behalf"),
icon: "calendar",
},
"write:event:delete": {
title: t("Delete events"),
text: t("This application will be allowed to delete events on your behalf"),
icon: "calendar",
},
"write:media:upload": {
title: t("Upload media"),
text: t("This application will be allowed to upload media on your behalf"),
icon: "image",
},
"write:media:remove": {
title: t("Remove uploaded media"),
text: t(
"This application will be allowed to remove uploaded media on your behalf"
),
icon: "image",
},
"write:group:post:create": {
title: t("Publish group posts"),
text: t(
"This application will be allowed to publish group posts on your behalf"
),
icon: "bullhorn",
},
"write:group:post:update": {
title: t("Update group posts"),
text: t(
"This application will be allowed to update group posts on your behalf"
),
icon: "bullhorn",
},
"write:group:post:delete": {
title: t("Delete group posts"),
text: t(
"This application will be allowed to delete group posts on your behalf"
),
icon: "bullhorn",
},
"read:group:resources": {
title: t("Access your group's resources"),
text: t(
"This application will be allowed to access all of the groups you're a member of on your behalf"
),
icon: "link",
},
"write:group:resources:create": {
title: t("Create group resources"),
text: t(
"This application will be allowed to create resources in all of the groups you're a member of on your behalf"
),
icon: "link",
},
"write:group:resources:update": {
title: t("Update group resources"),
text: t(
"This application will be allowed to update resources in all of the groups you're a member of on your behalf"
),
icon: "link",
},
"write:group:resources:delete": {
title: t("Delete group resources"),
text: t(
"This application will be allowed to delete resources in all of the groups you're a member of on your behalf"
),
icon: "link",
},
"read:group:events": {
title: t("Access group events"),
text: t(
"This application will be allowed to list and access group events in all of the groups you're a member of on your behalf"
),
icon: "calendar",
},
"read:group:discussions": {
title: t("Access group discussions"),
text: t(
"This application will be allowed to list and access group discussions in all of the groups you're a member of on your behalf"
),
icon: "chat",
},
"read:group:members": {
title: t("Access group members"),
text: t(
"This application will be allowed to list group members in all of the groups you're a member of on your behalf"
),
icon: "account-circle",
},
"read:group:followers": {
title: t("Access group followers"),
text: t(
"This application will be allowed to list group followers in all of the groups you're a member of on your behalf"
),
icon: "account-circle",
},
"read:group:activities": {
title: t("Access group activities"),
text: t(
"This application will be allowed to access group activities in all of the groups you're a member of on your behalf"
),
icon: "timeline-text",
},
"read:group:todo_lists": {
title: t("Access group todo-lists"),
text: t(
"This application will be allowed to list and access group todo-lists in all of the groups you're a member of on your behalf"
),
icon: "checkbox-marked",
},
"write:group:group_membership": {
title: t("Manage group memberships"),
text: t(
"This application will be allowed to join and leave groups on your behalf"
),
icon: "account-circle",
},
"write:group:members": {
title: t("Manage group members"),
text: t(
"This application will be allowed to manage group members in all of the groups you're a member of on your behalf"
),
icon: "account-circle",
},
"read:profile:organized_events": {
title: t("Access organized events"),
text: t(
"This application will be allowed to list and view your organized events"
),
icon: "calendar",
},
"read:profile:participations": {
title: t("Access participations"),
text: t(
"This application will be allowed to list and view the events you're participating to"
),
icon: "account-circle",
},
"read:profile:memberships": {
title: t("Access memberships"),
text: t(
"This application will be allowed to list and view the groups you're a member of"
),
icon: "account-circle",
},
"read:profile:follows": {
title: t("Access followed groups"),
text: t(
"This application will be allowed to list and view the groups you're following"
),
icon: "account-circle",
},
"write:profile:create": {
title: t("Create new profiles"),
text: t(
"This application will be allowed to create new profiles for your account on your behalf"
),
icon: "account-circle",
},
"write:profile:update": {
title: t("Update profiles"),
text: t(
"This application will be allowed to update your profiles on your behalf"
),
icon: "account-circle",
},
"write:profile:delete": {
title: t("Delete profiles"),
text: t(
"This application will be allowed to delete your profiles on your behalf"
),
icon: "account-circle",
},
"write:comment:create": {
title: t("Post comments"),
text: t("This application will be allowed to post comments on your behalf"),
icon: "comment",
},
"write:comment:update": {
title: t("Update comments"),
text: t(
"This application will be allowed to update comments on your behalf"
),
icon: "comment",
},
"write:comment:delete": {
title: t("Delete comments"),
text: t(
"This application will be allowed to delete comments on your behalf"
),
icon: "comment",
},
"write:group:discussion:create": {
title: t("Create group discussions"),
text: t(
"This application will be allowed to create group discussions on your behalf"
),
icon: "comment",
},
"write:group:discussion:update": {
title: t("Update group discussions"),
text: t(
"This application will be allowed to update group discussions on your behalf"
),
icon: "comment",
},
"write:group:discussion:delete": {
title: t("Delete group discussions"),
text: t(
"This application will be allowed to delete group discussions on your behalf"
),
icon: "comment",
},
"write:profile:feed_token:create": {
title: t("Create feed tokens"),
text: t(
"This application will be allowed to create feed tokens on your behalf"
),
icon: "rss",
},
"write:feed_token:delete": {
title: t("Delete feed tokens"),
text: t(
"This application will be allowed to delete feed tokens on your behalf"
),
icon: "rss",
},
"write:participation": {
title: t("Manage event participations"),
text: t(
"This application will be allowed to manage events participations on your behalf"
),
icon: "rss",
},
"write:user:setting:activity": {
title: t("Manage activity settings"),
text: t(
"This application will be allowed to manage your account activity settings"
),
icon: "cog",
},
"write:user:setting:push": {
title: t("Manage push notification settings"),
text: t(
"This application will be allowed to manage your account push notification settings"
),
icon: "cog",
},
};

View File

@@ -250,6 +250,14 @@ const icons: Record<string, () => Promise<any>> = {
),
ExitToApp: () =>
import(`../../../node_modules/vue-material-design-icons/ExitToApp.vue`),
CheckboxMarked: () =>
import(
`../../../node_modules/vue-material-design-icons/CheckboxMarked.vue`
),
EyeOutline: () =>
import(`../../../node_modules/vue-material-design-icons/EyeOutline.vue`),
PencilOutline: () =>
import(`../../../node_modules/vue-material-design-icons/PencilOutline.vue`),
};
const props = withDefaults(

View File

@@ -5,6 +5,7 @@ import {
PERSON_STATUS_GROUP,
} from "@/graphql/actor";
import { IPerson } from "@/types/actor";
import { ICurrentUser } from "@/types/current-user.model";
import { useQuery } from "@vue/apollo-composable";
import { computed, Ref, unref } from "vue";
import { useCurrentUserClient } from "./user";
@@ -24,7 +25,7 @@ export function useCurrentActorClient() {
export function useCurrentUserIdentities() {
const { currentUser } = useCurrentUserClient();
const { result, error, loading } = useQuery<{ identities: IPerson[] }>(
const { result, error, loading } = useQuery<{ loggedUser: Pick<ICurrentUser, 'actors'> }>(
IDENTITIES,
{},
() => ({
@@ -35,7 +36,7 @@ export function useCurrentUserIdentities() {
})
);
const identities = computed(() => result.value?.identities);
const identities = computed(() => result.value?.loggedUser?.actors);
return { identities, error, loading };
}

View File

@@ -82,11 +82,11 @@ export function registerAccount() {
{ context }
) => {
if (context?.userAlreadyActivated) {
const identitiesData = store.readQuery<{ identities: IPerson[] }>({
const currentUserData = store.readQuery<{ loggedUser: Pick<ICurrentUser, 'actors'> }>({
query: IDENTITIES,
});
if (identitiesData && localData) {
if (currentUserData && localData) {
const newPersonData = {
...localData.registerPerson,
type: ActorType.PERSON,
@@ -95,8 +95,10 @@ export function registerAccount() {
store.writeQuery({
query: IDENTITIES,
data: {
...identitiesData,
identities: [...identitiesData.identities, newPersonData],
...currentUserData.loggedUser,
actors: [
[...currentUserData.loggedUser.actors, newPersonData]
]
},
});
}

View File

@@ -282,13 +282,17 @@ export const LOGGED_USER_MEMBERSHIPS = gql`
export const IDENTITIES = gql`
query Identities {
identities {
...ActorFragment
loggedUser {
actors {
...ActorFragment
}
}
}
${ACTOR_FRAGMENT}
`;
export const PERSON_MEMBERSHIPS = gql`
query PersonMemberships($id: ID!) {
person(id: $id) {

View File

@@ -14,9 +14,9 @@ export const AUTH_APPLICATION = gql`
export const AUTORIZE_APPLICATION = gql`
mutation AuthorizeApplication(
$applicationClientId: String!
$redirectURI: String
$redirectURI: String!
$state: String
$scope: String
$scope: String!
) {
authorizeApplication(
clientId: $applicationClientId
@@ -26,6 +26,23 @@ export const AUTORIZE_APPLICATION = gql`
) {
code
state
clientId
scope
}
}
`;
export const AUTORIZE_DEVICE_APPLICATION = gql`
mutation AuthorizeDeviceApplication(
$applicationClientId: String!
$userCode: String
) {
authorizeDeviceApplication(
clientId: $applicationClientId
userCode: $userCode
) {
clientId
scope
}
}
`;

View File

@@ -72,6 +72,7 @@ export const CONFIG = gql`
features {
groups
eventCreation
antispam
}
restrictions {
onlyAdminCanCreateGroups

View File

@@ -226,15 +226,6 @@ export const FETCH_GROUP = gql`
${RESOURCE_METADATA_BASIC_FIELDS_FRAGMENT}
`;
export const FETCH_GROUP_BY_ID = gql`
query FetchGroupById($id: ID!) {
groupById(id: $name) {
...GroupFullFields
}
}
${GROUP_FIELDS_FRAGMENTS}
`;
export const GET_GROUP = gql`
query GetGroup(
$id: ID!
@@ -407,6 +398,7 @@ export const GROUP_TIMELINE = gql`
openness
physicalAddress {
id
originId
}
banner {
id

View File

@@ -1457,5 +1457,101 @@
"Autorize this application to access your account?": "Autorize this application to access your account?",
"This application will be able to access all of your informations and post content on your behalf. Make sure you only approve applications you trust.": "This application will be able to access all of your informations and post content on your behalf. Make sure you only approve applications you trust.",
"Authorize application": "Authorize application",
"Authorize": "Authorize"
"Authorize": "Authorize",
"You'll be able to revoke access for this application in your account settings.": "You'll be able to revoke access for this application in your account settings.",
"Read all of your account's data": "Read all of your account's data",
"This application will be allowed to see all of your events organized, the events you participate to, …": "",
"Modify all of your account's data": "Modify all of your account's data",
"This application will be allowed to publish events on your behalf, participate to events": "",
"Publish events": "Publish events",
"This application will be allowed to publish events on your behalf": "This application will be allowed to publish events on your behalf",
"Update events": "Update events",
"This application will be allowed to update events on your behalf": "This application will be allowed to update events on your behalf",
"Delete events": "Delete events",
"This application will be allowed to delete events on your behalf": "This application will be allowed to delete events on your behalf",
"Upload media": "Upload media",
"This application will be allowed to upload media on your behalf": "This application will be allowed to upload media on your behalf",
"Remove uploaded media": "Remove uploaded media",
"This application will be allowed to remove uploaded media on your behalf": "This application will be allowed to remove uploaded media on your behalf",
"Publish group posts": "Publish group posts",
"This application will be allowed to publish group posts on your behalf": "This application will be allowed to publish group posts on your behalf",
"Update group posts": "Update group posts",
"This application will be allowed to update group posts on your behalf": "This application will be allowed to update group posts on your behalf",
"Delete group posts": "Delete group posts",
"This application will be allowed to delete group posts on your behalf": "This application will be allowed to delete group posts on your behalf",
"Access your group's resources": "Access your group's resources",
"This application will be allowed to access all of the groups you're a member of on your behalf": "This application will be allowed to access all of the groups you're a member of on your behalf",
"Create group resources": "Create group resources",
"This application will be allowed to create resources in all of the groups you're a member of on your behalf": "This application will be allowed to create resources in all of the groups you're a member of on your behalf",
"Update group resources": "Update group resources",
"This application will be allowed to update resources in all of the groups you're a member of on your behalf": "This application will be allowed to update resources in all of the groups you're a member of on your behalf",
"Delete group resources": "Delete group resources",
"This application will be allowed to delete resources in all of the groups you're a member of on your behalf": "This application will be allowed to delete resources in all of the groups you're a member of on your behalf",
"Access group events": "Access group events",
"This application will be allowed to list and access group events in all of the groups you're a member of on your behalf": "This application will be allowed to list and access group events in all of the groups you're a member of on your behalf",
"Access group discussions": "Access group discussions",
"This application will be allowed to list and access group discussions in all of the groups you're a member of on your behalf": "This application will be allowed to list and access group discussions in all of the groups you're a member of on your behalf",
"Access group members": "Access group members",
"This application will be allowed to list group members in all of the groups you're a member of on your behalf": "This application will be allowed to list group members in all of the groups you're a member of on your behalf",
"Access group followers": "Access group followers",
"This application will be allowed to list group followers in all of the groups you're a member of on your behalf": "This application will be allowed to list group followers in all of the groups you're a member of on your behalf",
"Access group activities": "Access group activities",
"This application will be allowed to access group activities in all of the groups you're a member of on your behalf": "This application will be allowed to access group activities in all of the groups you're a member of on your behalf",
"Access group todo-lists": "Access group todo-lists",
"This application will be allowed to list and access group todo-lists in all of the groups you're a member of on your behalf": "This application will be allowed to list and access group todo-lists in all of the groups you're a member of on your behalf",
"Manage group memberships": "Manage group memberships",
"This application will be allowed to join and leave groups on your behalf": "This application will be allowed to join and leave groups on your behalf",
"Manage group members": "Manage group members",
"This application will be allowed to manage group members in all of the groups you're a member of on your behalf": "This application will be allowed to manage group members in all of the groups you're a member of on your behalf",
"Access organized events": "Access organized events",
"This application will be allowed to list and view your organized events": "This application will be allowed to list and view your organized events",
"Access participations": "Access participations",
"This application will be allowed to list and view the events you're participating to": "This application will be allowed to list and view the events you're participating to",
"Access memberships": "Access memberships",
"This application will be allowed to list and view the groups you're a member of": "This application will be allowed to list and view the groups you're a member of",
"Access followed groups": "Access followed groups",
"This application will be allowed to list and view the groups you're following": "This application will be allowed to list and view the groups you're following",
"Create new profiles": "Create new profiles",
"This application will be allowed to create new profiles for your account on your behalf": "This application will be allowed to create new profiles for your account on your behalf",
"Update profiles": "Update profiles",
"This application will be allowed to update your profiles on your behalf": "This application will be allowed to update your profiles on your behalf",
"Delete profiles": "Delete profiles",
"This application will be allowed to delete your profiles on your behalf": "This application will be allowed to delete your profiles on your behalf",
"Post comments": "Post comments",
"This application will be allowed to post comments on your behalf": "This application will be allowed to post comments on your behalf",
"Update comments": "Update comments",
"This application will be allowed to update comments on your behalf": "This application will be allowed to update comments on your behalf",
"Delete comments": "Delete comments",
"This application will be allowed to delete comments on your behalf": "This application will be allowed to delete comments on your behalf",
"Create group discussions": "Create group discussions",
"This application will be allowed to create group discussions on your behalf": "This application will be allowed to create group discussions on your behalf",
"Update group discussions": "Update group discussions",
"This application will be allowed to update group discussions on your behalf": "This application will be allowed to update group discussions on your behalf",
"Delete group discussions": "Delete group discussions",
"This application will be allowed to delete group discussions on your behalf": "This application will be allowed to delete group discussions on your behalf",
"Create feed tokens": "Create feed tokens",
"This application will be allowed to create feed tokens on your behalf": "This application will be allowed to create feed tokens on your behalf",
"Delete feed tokens": "Delete feed tokens",
"This application will be allowed to delete feed tokens on your behalf": "This application will be allowed to delete feed tokens on your behalf",
"Manage event participations": "Manage event participations",
"This application will be allowed to manage events participations on your behalf": "This application will be allowed to manage events participations on your behalf",
"Manage activity settings": "Manage activity settings",
"This application will be allowed to manage your account activity settings": "This application will be allowed to manage your account activity settings",
"Manage push notification settings": "Manage push notification settings",
"This application will be allowed to manage your account push notification settings": "This application will be allowed to manage your account push notification settings",
"Apps": "Apps",
"Device activation": "Device activation",
"Application not found": "Application not found",
"The provided application was not found.": "The provided application was not found.",
"Your application code": "Your application code",
"You need to provide the following code to your application": "You need to provide the following code to your application",
"Enter the code displayed on your device": "Enter the code displayed on your device",
"Continue": "Continue",
"The device code is incorrect or no longer valid.": "The device code is incorrect or no longer valid.",
"These apps can access your account through the API. If you see here apps that you don't recognize, that don't work as expected or that you don't use anymore, you can revoke their access.": "These apps can access your account through the API. If you see here apps that you don't recognize, that don't work as expected or that you don't use anymore, you can revoke their access.",
"Last used on {last_used_date}": "Last used on {last_used_date}",
"Never used": "Never used",
"Authorized on {authorization_date}": "Authorized on {authorization_date}",
"Revoke": "Revoke",
"Application was revoked": "Application was revoked"
}

View File

@@ -1455,5 +1455,99 @@
"Autorize this application to access your account?": "Autoriser cette application à accéder à votre compte ?",
"This application will be able to access all of your informations and post content on your behalf. Make sure you only approve applications you trust.": "Cette application sera capable d'accéder à toutes vos informations et poster du contenu en votre nom. Assurez-vous d'approuver uniquement des applications en lesquelles vous avez confiance.",
"Authorize application": "Autoriser l'application",
"Authorize": "Autoriser"
"Authorize": "Autoriser",
"You'll be able to revoke access for this application in your account settings.": "Vous pourrez révoquer l'accès pour cette applications dans les paramètres de votre compte.",
"Read all of your account's data": "Lire toutes les données de votre compte",
"Modify all of your account's data": "Modifier toutes les données de votre compte",
"Publish events": "Publier des événements",
"This application will be allowed to publish events on your behalf": "Cette application pourra publier des événements en votre nom",
"Update events": "Update events",
"This application will be allowed to update events on your behalf": "Cette application pourra mettre à jour des événements en votre nom",
"Delete events": "Delete events",
"This application will be allowed to delete events on your behalf": "Cette application pourra supprimer des événements en votre nom",
"Upload media": "Upload media",
"This application will be allowed to upload media on your behalf": "Cette application pourra téléverser des médias en votre nom",
"Remove uploaded media": "Remove uploaded media",
"This application will be allowed to remove uploaded media on your behalf": "Cette application pourra supprimer des médias téléversés en votre nom",
"Publish group posts": "Publish group posts",
"This application will be allowed to publish group posts on your behalf": "Cette application pourra publier des billets de groupes en votre nom",
"Update group posts": "Update group posts",
"This application will be allowed to update group posts on your behalf": "Cette application pourra mettre à jour des billets de groupes en votre nom",
"Delete group posts": "Delete group posts",
"This application will be allowed to delete group posts on your behalf": "Cette application pourra supprimer des billets de groupes en votre nom",
"Access your group's resources": "Access your group's resources",
"This application will be allowed to access all of the groups you're a member of on your behalf": "Cette application pourra accéder à tous les groupes dont vous êtes membres",
"Create group resources": "Create group resources",
"This application will be allowed to create resources in all of the groups you're a member of on your behalf": "Cette application pourra créer des ressources dans chacun des groupes dont vous êtes membre en votre nom",
"Update group resources": "Update group resources",
"This application will be allowed to update resources in all of the groups you're a member of on your behalf": "Cette application pourra mettre à jour des ressources dans chacun des groupes dont vous êtes membre en votre nom",
"Delete group resources": "Delete group resources",
"This application will be allowed to delete resources in all of the groups you're a member of on your behalf": "Cette application pourra supprimer des ressources dans chacun des groupes dont vous êtes membre en votre nom",
"Access group events": "Access group events",
"This application will be allowed to list and access group events in all of the groups you're a member of on your behalf": "Cette application pourra lister et accéder aux événements des groupes dont vous êtes membre",
"Access group discussions": "Access group discussions",
"This application will be allowed to list and access group discussions in all of the groups you're a member of on your behalf": "This application will be allowed to list and access group discussions in all of the groups you're a member of on your behalf",
"Access group members": "Access group members",
"This application will be allowed to list group members in all of the groups you're a member of on your behalf": "This application will be allowed to list group members in all of the groups you're a member of on your behalf",
"Access group followers": "Access group followers",
"This application will be allowed to list group followers in all of the groups you're a member of on your behalf": "This application will be allowed to list group followers in all of the groups you're a member of on your behalf",
"Access group activities": "Access group activities",
"This application will be allowed to access group activities in all of the groups you're a member of on your behalf": "This application will be allowed to access group activities in all of the groups you're a member of on your behalf",
"Access group todo-lists": "Access group todo-lists",
"This application will be allowed to list and access group todo-lists in all of the groups you're a member of on your behalf": "This application will be allowed to list and access group todo-lists in all of the groups you're a member of on your behalf",
"Manage group memberships": "Manage group memberships",
"This application will be allowed to join and leave groups on your behalf": "This application will be allowed to join and leave groups on your behalf",
"Manage group members": "Manage group members",
"This application will be allowed to manage group members in all of the groups you're a member of on your behalf": "This application will be allowed to manage group members in all of the groups you're a member of on your behalf",
"Access organized events": "Access organized events",
"This application will be allowed to list and view your organized events": "This application will be allowed to list and view your organized events",
"Access participations": "Access participations",
"This application will be allowed to list and view the events you're participating to": "This application will be allowed to list and view the events you're participating to",
"Access memberships": "Access memberships",
"This application will be allowed to list and view the groups you're a member of": "This application will be allowed to list and view the groups you're a member of",
"Access followed groups": "Access followed groups",
"This application will be allowed to list and view the groups you're following": "This application will be allowed to list and view the groups you're following",
"Create new profiles": "Create new profiles",
"This application will be allowed to create new profiles for your account on your behalf": "This application will be allowed to create new profiles for your account on your behalf",
"Update profiles": "Update profiles",
"This application will be allowed to update your profiles on your behalf": "This application will be allowed to update your profiles on your behalf",
"Delete profiles": "Delete profiles",
"This application will be allowed to delete your profiles on your behalf": "This application will be allowed to delete your profiles on your behalf",
"Post comments": "Post comments",
"This application will be allowed to post comments on your behalf": "This application will be allowed to post comments on your behalf",
"Update comments": "Update comments",
"This application will be allowed to update comments on your behalf": "This application will be allowed to update comments on your behalf",
"Delete comments": "Delete comments",
"This application will be allowed to delete comments on your behalf": "This application will be allowed to delete comments on your behalf",
"Create group discussions": "Create group discussions",
"This application will be allowed to create group discussions on your behalf": "This application will be allowed to create group discussions on your behalf",
"Update group discussions": "Update group discussions",
"This application will be allowed to update group discussions on your behalf": "This application will be allowed to update group discussions on your behalf",
"Delete group discussions": "Delete group discussions",
"This application will be allowed to delete group discussions on your behalf": "This application will be allowed to delete group discussions on your behalf",
"Create feed tokens": "Create feed tokens",
"This application will be allowed to create feed tokens on your behalf": "This application will be allowed to create feed tokens on your behalf",
"Delete feed tokens": "Delete feed tokens",
"This application will be allowed to delete feed tokens on your behalf": "This application will be allowed to delete feed tokens on your behalf",
"Manage event participations": "Manage event participations",
"This application will be allowed to manage events participations on your behalf": "This application will be allowed to manage events participations on your behalf",
"Manage activity settings": "Manage activity settings",
"This application will be allowed to manage your account activity settings": "This application will be allowed to manage your account activity settings",
"Manage push notification settings": "Manage push notification settings",
"This application will be allowed to manage your account push notification settings": "This application will be allowed to manage your account push notification settings",
"Apps": "Apps",
"Device activation": "Device activation",
"Application not found": "Application not found",
"The provided application was not found.": "The provided application was not found.",
"Your application code": "Your application code",
"You need to provide the following code to your application": "You need to provide the following code to your application",
"Enter the code displayed on your device": "Enter the code displayed on your device",
"Continue": "Continue",
"The device code is incorrect or no longer valid.": "The device code is incorrect or no longer valid.",
"These apps can access your account through the API. If you see here apps that you don't recognize, that don't work as expected or that you don't use anymore, you can revoke their access.": "These apps can access your account through the API. If you see here apps that you don't recognize, that don't work as expected or that you don't use anymore, you can revoke their access.",
"Last used on {last_used_date}": "Last used on {last_used_date}",
"Never used": "Never used",
"Authorized on {authorization_date}": "Authorized on {authorization_date}",
"Revoke": "Revoke",
"Application was revoked": "Application was revoked"
}

View File

@@ -3,7 +3,7 @@ export interface IApplication {
clientId: string;
clientSecret?: string;
redirectUris?: string;
scopes: string | null;
scope: string | null;
website: string | null;
}

View File

@@ -15,6 +15,7 @@ export interface ICurrentUser {
isLoggedIn: boolean;
role: ICurrentUserRole;
defaultActor?: IPerson;
actors: IPerson[];
}
export interface IUserPreferredLocation {

View File

@@ -1,6 +1,7 @@
import { AUTH_USER_ACTOR_ID } from "@/constants";
import { UPDATE_CURRENT_ACTOR_CLIENT, IDENTITIES } from "@/graphql/actor";
import { IPerson } from "@/types/actor";
import { ICurrentUser } from "@/types/current-user.model";
import { apolloClient } from "@/vue-apollo";
import {
provideApolloClient,
@@ -36,10 +37,10 @@ export async function initializeCurrentActor(): Promise<void> {
const actorId = localStorage.getItem(AUTH_USER_ACTOR_ID);
const { result: identitiesResult } = provideApolloClient(apolloClient)(() =>
useQuery<{ identities: IPerson[] }>(IDENTITIES)
useQuery<{ currentUser: Pick<ICurrentUser, 'actors'> }>(IDENTITIES)
);
const identities = computed(() => identitiesResult.value?.identities);
const identities = computed(() => identitiesResult.value?.currentUser.actors);
watch(identities, async () => {
if (identities.value && identities.value.length < 1) {

View File

@@ -234,6 +234,7 @@ import { convertToUsername } from "@/utils/username";
import { Dialog } from "@/plugins/dialog";
import { Notifier } from "@/plugins/notifier";
import { AbsintheGraphQLErrors } from "@/types/errors.model";
import { ICurrentUser } from "@/types/current-user.model";
const { t } = useI18n({ useScope: "global" });
const router = useRouter();
@@ -348,7 +349,7 @@ const {
onError: deletePersonError,
} = useMutation(DELETE_PERSON, () => ({
update: (store: ApolloCache<InMemoryCache>) => {
const data = store.readQuery<{ identities: IPerson[] }>({
const data = store.readQuery<{ loggedUser: Pick<ICurrentUser, 'actors'> }>({
query: IDENTITIES,
});
@@ -356,7 +357,10 @@ const {
store.writeQuery({
query: IDENTITIES,
data: {
identities: data.identities.filter((i) => i.id !== identity.value.id),
loggedUser: {
...data.loggedUser,
actors: data.loggedUser.actors.filter((i) => i.id !== identity.value.id)
}
},
});
}
@@ -379,10 +383,10 @@ deletePersonDone(async () => {
*/
const client = resolveClient();
const data = client.readQuery<{
identities: IPerson[];
loggedUser: Pick<ICurrentUser, 'actors'>
}>({ query: IDENTITIES });
if (data) {
await maybeUpdateCurrentActorCache(data.identities[0]);
await maybeUpdateCurrentActorCache(data.loggedUser.actors[0]);
}
await redirectIfNoIdentitySelected();
@@ -408,7 +412,7 @@ const {
store: ApolloCache<InMemoryCache>,
{ data: updateData }: FetchResult
) => {
const data = store.readQuery<{ identities: IPerson[] }>({
const data = store.readQuery<{ loggedUser: Pick<ICurrentUser, 'actors'> }>({
query: IDENTITIES,
});
@@ -452,7 +456,7 @@ const {
store: ApolloCache<InMemoryCache>,
{ data: updateData }: FetchResult
) => {
const data = store.readQuery<{ identities: IPerson[] }>({
const data = store.readQuery<{ loggedUser: Pick<ICurrentUser, 'actors'> }>({
query: IDENTITIES,
});
@@ -460,10 +464,13 @@ const {
store.writeQuery({
query: IDENTITIES,
data: {
identities: [
...data.identities,
loggedUser: {
...data.loggedUser,
actors: [
...data.loggedUser.actors,
{ ...updateData?.createPerson, type: ActorType.PERSON },
],
]
}
},
});
}

View File

@@ -18,27 +18,47 @@
<section class="timeline">
<o-field>
<o-radio v-model="activityType" :native-value="undefined">
<o-radio class="pr-4" v-model="activityType" :native-value="undefined">
<TimelineText />
{{ t("All activities") }}</o-radio
>
<o-radio v-model="activityType" :native-value="ActivityType.MEMBER">
<o-radio
class="pr-4"
v-model="activityType"
:native-value="ActivityType.MEMBER"
>
<o-icon icon="account-multiple-plus"></o-icon>
{{ t("Members") }}</o-radio
>
<o-radio v-model="activityType" :native-value="ActivityType.GROUP">
<o-radio
class="pr-4"
v-model="activityType"
:native-value="ActivityType.GROUP"
>
<o-icon icon="cog"></o-icon>
{{ t("Settings") }}</o-radio
>
<o-radio v-model="activityType" :native-value="ActivityType.EVENT">
<o-radio
class="pr-4"
v-model="activityType"
:native-value="ActivityType.EVENT"
>
<o-icon icon="calendar"></o-icon>
{{ t("Events") }}</o-radio
>
<o-radio v-model="activityType" :native-value="ActivityType.POST">
<o-radio
class="pr-4"
v-model="activityType"
:native-value="ActivityType.POST"
>
<o-icon icon="bullhorn"></o-icon>
{{ t("Posts") }}</o-radio
>
<o-radio v-model="activityType" :native-value="ActivityType.DISCUSSION">
<o-radio
class="pr-4"
v-model="activityType"
:native-value="ActivityType.DISCUSSION"
>
<o-icon icon="chat"></o-icon>
{{ t("Discussions") }}</o-radio
>
@@ -48,11 +68,16 @@
>
</o-field>
<o-field>
<o-radio v-model="activityAuthor" :native-value="undefined">
<o-radio
class="pr-4"
v-model="activityAuthor"
:native-value="undefined"
>
<TimelineText />
{{ t("All activities") }}</o-radio
>
<o-radio
class="pr-4"
v-model="activityAuthor"
:native-value="ActivityAuthorFilter.SELF"
>
@@ -60,6 +85,7 @@
{{ t("From yourself") }}</o-radio
>
<o-radio
class="pr-4"
v-model="activityAuthor"
:native-value="ActivityAuthorFilter.BY"
>
@@ -89,7 +115,7 @@
<h2 v-else>
{{ formatDateString(date) }}
</h2>
<ul>
<ul class="before:opacity-10">
<li v-for="activityItem in activityItems" :key="activityItem.id">
<skeleton-activity-item v-if="activityItem.type === 'skeleton'" />
<component
@@ -202,6 +228,7 @@ const page = ref(1);
const {
result: groupTimelineResult,
fetchMore: fetchMoreActivities,
onError: onGroupTLError,
loading,
} = useQuery<{ group: IGroup }>(GROUP_TIMELINE, () => ({
preferredUsername: props.preferredUsername,
@@ -211,6 +238,8 @@ const {
author: activityAuthor.value,
}));
onGroupTLError((err) => console.error(err));
const group = computed(() => groupTimelineResult.value?.group);
useHead({

View File

@@ -36,8 +36,8 @@
/>
<div v-show="authApplicationError">
<div
class="rounded-lg bg-mbz-danger shadow-xl my-6 p-4 flex items-center gap-2"
v-if="authApplicationGraphError?.status_code === 404"
class="rounded-lg text-white bg-mbz-danger shadow-xl my-6 p-4 flex items-center gap-2"
v-if="authApplicationGraphError?.message === 'not_found'"
>
<AlertCircle :size="42" />
<div>
@@ -72,9 +72,6 @@
<p class="text-4xl">{{ resultCode }}</p>
</div>
</div>
<o-button variant="text" tag="router-link" :to="{ name: RouteName.HOME }">{{
t("Back to homepage")
}}</o-button>
</div>
</template>
@@ -83,8 +80,8 @@ import { useRouteQuery } from "vue-use-route-query";
import { useHead } from "@vueuse/head";
import { computed, ref } from "vue";
import { useI18n } from "vue-i18n";
import { useMutation, useQuery } from "@vue/apollo-composable";
import { AUTH_APPLICATION, AUTORIZE_APPLICATION } from "@/graphql/application";
import { useQuery } from "@vue/apollo-composable";
import { AUTH_APPLICATION } from "@/graphql/application";
import { IApplication } from "@/types/application.model";
import AlertCircle from "vue-material-design-icons/AlertCircle.vue";
import type { AbsintheGraphQLError } from "@/types/errors.model";
@@ -98,7 +95,6 @@ const redirectURI = useRouteQuery("redirect_uri", null);
const state = useRouteQuery("state", null);
const scope = useRouteQuery("scope", null);
const OUT_OF_BAND_REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob";
const resultCode = ref<string | null>(null);
const {
@@ -123,44 +119,6 @@ const authApplicationGraphError = computed(
() => authApplicationError.value?.graphQLErrors[0] as AbsintheGraphQLError
);
const { mutate: authorizeMutation, onDone: onAuthorizeMutationDone } =
useMutation<
{ authorizeApplication: { code: string; state: string } },
{
applicationClientId: string;
redirectURI: string;
state?: string | null;
scope?: string | null;
}
>(AUTORIZE_APPLICATION);
const authorize = () => {
authorizeMutation({
applicationClientId: clientId.value as string,
redirectURI: redirectURI.value as string,
state: state.value,
scope: scope.value,
});
};
onAuthorizeMutationDone(({ data }) => {
const code = data?.authorizeApplication?.code;
const returnedState = data?.authorizeApplication?.state ?? "";
if (!code) return;
if (redirectURI.value !== OUT_OF_BAND_REDIRECT_URI) {
const params = new URLSearchParams(
Object.entries({ code, state: returnedState })
);
window.location.assign(
new URL(`${redirectURI.value}?${params.toString()}`)
);
return;
}
resultCode.value = code;
});
useHead({
title: computed(() => t("Authorize application")),
});

View File

@@ -136,10 +136,8 @@ const { mutate: revoke, onDone: onRevokedApplication } = useMutation<
const notifier = inject<Notifier>("notifier");
onRevokedApplication(() => {
notifier?.success(
t("Application was revoked")
);
})
notifier?.success(t("Application was revoked"));
});
useHead({
title: computed(() => t("Apps")),