Introduce authorizations with Rajska
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
b6875f6a4b
commit
8984bd7636
|
@ -78,7 +78,7 @@ config :tesla, Mobilizon.Service.HTTP.HostMetaClient,
|
||||||
|
|
||||||
config :mobilizon, Mobilizon.Service.Geospatial, service: Mobilizon.Service.Geospatial.Mock
|
config :mobilizon, Mobilizon.Service.Geospatial, service: Mobilizon.Service.Geospatial.Mock
|
||||||
|
|
||||||
config :mobilizon, Oban, queues: false, plugins: false
|
config :mobilizon, Oban, testing: :manual
|
||||||
|
|
||||||
config :mobilizon, Mobilizon.Web.Auth.Guardian, secret_key: "some secret"
|
config :mobilizon, Mobilizon.Web.Auth.Guardian, secret_key: "some secret"
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
"@tiptap/extension-strike": "^2.0.0-beta.26",
|
"@tiptap/extension-strike": "^2.0.0-beta.26",
|
||||||
"@tiptap/extension-text": "^2.0.0-beta.15",
|
"@tiptap/extension-text": "^2.0.0-beta.15",
|
||||||
"@tiptap/extension-underline": "^2.0.0-beta.7",
|
"@tiptap/extension-underline": "^2.0.0-beta.7",
|
||||||
|
"@tiptap/pm": "^2.0.0-beta.220",
|
||||||
"@tiptap/suggestion": "^2.0.0-beta.195",
|
"@tiptap/suggestion": "^2.0.0-beta.195",
|
||||||
"@tiptap/vue-3": "^2.0.0-beta.96",
|
"@tiptap/vue-3": "^2.0.0-beta.96",
|
||||||
"@vue-a11y/announcer": "^2.1.0",
|
"@vue-a11y/announcer": "^2.1.0",
|
||||||
|
@ -59,7 +60,7 @@
|
||||||
"autoprefixer": "^10",
|
"autoprefixer": "^10",
|
||||||
"blurhash": "^2.0.0",
|
"blurhash": "^2.0.0",
|
||||||
"date-fns": "^2.16.0",
|
"date-fns": "^2.16.0",
|
||||||
"date-fns-tz": "^1.1.6",
|
"date-fns-tz": "^2.0.0",
|
||||||
"floating-vue": "^2.0.0-beta.17",
|
"floating-vue": "^2.0.0-beta.17",
|
||||||
"graphql": "^15.8.0",
|
"graphql": "^15.8.0",
|
||||||
"graphql-tag": "^2.10.3",
|
"graphql-tag": "^2.10.3",
|
||||||
|
@ -74,16 +75,6 @@
|
||||||
"p-debounce": "^4.0.0",
|
"p-debounce": "^4.0.0",
|
||||||
"phoenix": "^1.6",
|
"phoenix": "^1.6",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"prosemirror-commands": "^1.5.0",
|
|
||||||
"prosemirror-dropcursor": "^1.6.1",
|
|
||||||
"prosemirror-gapcursor": "^1.3.1",
|
|
||||||
"prosemirror-history": "^1.3.0",
|
|
||||||
"prosemirror-keymap": "^1.2.0",
|
|
||||||
"prosemirror-model": "^1.19.0",
|
|
||||||
"prosemirror-schema-list": "^1.2.2",
|
|
||||||
"prosemirror-state": "^1.4.2",
|
|
||||||
"prosemirror-transform": "^1.7.1",
|
|
||||||
"prosemirror-view": "^1.30.0",
|
|
||||||
"register-service-worker": "^1.7.2",
|
"register-service-worker": "^1.7.2",
|
||||||
"sanitize-html": "^2.5.3",
|
"sanitize-html": "^2.5.3",
|
||||||
"tailwindcss": "^3",
|
"tailwindcss": "^3",
|
||||||
|
@ -100,7 +91,7 @@
|
||||||
"zhyswan-vuedraggable": "^4.1.3"
|
"zhyswan-vuedraggable": "^4.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@histoire/plugin-vue": "^0.12.4",
|
"@histoire/plugin-vue": "^0.15.8",
|
||||||
"@playwright/test": "^1.25.1",
|
"@playwright/test": "^1.25.1",
|
||||||
"@rushstack/eslint-patch": "^1.1.4",
|
"@rushstack/eslint-patch": "^1.1.4",
|
||||||
"@tailwindcss/forms": "^0.5.2",
|
"@tailwindcss/forms": "^0.5.2",
|
||||||
|
@ -114,8 +105,8 @@
|
||||||
"@types/phoenix": "^1.5.2",
|
"@types/phoenix": "^1.5.2",
|
||||||
"@types/sanitize-html": "^2.5.0",
|
"@types/sanitize-html": "^2.5.0",
|
||||||
"@vitejs/plugin-vue": "^4.0.0",
|
"@vitejs/plugin-vue": "^4.0.0",
|
||||||
"@vitest/coverage-c8": "^0.28.2",
|
"@vitest/coverage-c8": "^0.29.2",
|
||||||
"@vitest/ui": "^0.28.2",
|
"@vitest/ui": "^0.29.2",
|
||||||
"@vue/eslint-config-prettier": "^7.0.0",
|
"@vue/eslint-config-prettier": "^7.0.0",
|
||||||
"@vue/eslint-config-typescript": "^11.0.0",
|
"@vue/eslint-config-typescript": "^11.0.0",
|
||||||
"@vue/test-utils": "^2.0.2",
|
"@vue/test-utils": "^2.0.2",
|
||||||
|
@ -125,7 +116,7 @@
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"eslint-plugin-vue": "^9.3.0",
|
"eslint-plugin-vue": "^9.3.0",
|
||||||
"flush-promises": "^1.0.2",
|
"flush-promises": "^1.0.2",
|
||||||
"histoire": "^0.12.4",
|
"histoire": "^0.15.8",
|
||||||
"jsdom": "^21.1.0",
|
"jsdom": "^21.1.0",
|
||||||
"mock-apollo-client": "^1.1.0",
|
"mock-apollo-client": "^1.1.0",
|
||||||
"prettier": "^2.2.1",
|
"prettier": "^2.2.1",
|
||||||
|
@ -135,7 +126,7 @@
|
||||||
"typescript": "~4.9.4",
|
"typescript": "~4.9.4",
|
||||||
"vite": "^4.0.4",
|
"vite": "^4.0.4",
|
||||||
"vite-plugin-pwa": "^0.14.1",
|
"vite-plugin-pwa": "^0.14.1",
|
||||||
"vitest": "^0.28.2",
|
"vitest": "^0.29.2",
|
||||||
"vue-i18n-extract": "^2.0.4"
|
"vue-i18n-extract": "^2.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,18 +65,15 @@
|
||||||
</o-field>
|
</o-field>
|
||||||
<o-modal
|
<o-modal
|
||||||
has-modal-card
|
has-modal-card
|
||||||
v-model="showNewElementModal"
|
v-model:active="showNewElementModal"
|
||||||
:close-button-aria-label="$t('Close')"
|
:close-button-aria-label="$t('Close')"
|
||||||
>
|
>
|
||||||
<div class="modal-card">
|
<div class="">
|
||||||
<header class="modal-card-head">
|
<header class="">
|
||||||
<button
|
<h2>{{ t('Create a new metadata element') }}</h2>
|
||||||
type="button"
|
<p>{{ t('You can put any arbitrary content in this element. URLs will be clickable.') }}</p>
|
||||||
class="delete"
|
|
||||||
@click="showNewElementModal = false"
|
|
||||||
/>
|
|
||||||
</header>
|
</header>
|
||||||
<div class="modal-card-body">
|
<div class="">
|
||||||
<form @submit="addNewElement">
|
<form @submit="addNewElement">
|
||||||
<o-field :label="$t('Element title')">
|
<o-field :label="$t('Element title')">
|
||||||
<o-input v-model="newElement.title" />
|
<o-input v-model="newElement.title" />
|
||||||
|
@ -84,7 +81,7 @@
|
||||||
<o-field :label="$t('Element value')">
|
<o-field :label="$t('Element value')">
|
||||||
<o-input v-model="newElement.value" />
|
<o-input v-model="newElement.value" />
|
||||||
</o-field>
|
</o-field>
|
||||||
<o-button variant="primary" native-type="submit">{{
|
<o-button class="mt-2" variant="primary" native-type="submit">{{
|
||||||
$t("Add")
|
$t("Add")
|
||||||
}}</o-button>
|
}}</o-button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -70,7 +70,7 @@ function setupApp({ app }) {
|
||||||
new Promise((resolve) =>
|
new Promise((resolve) =>
|
||||||
resolve({
|
resolve({
|
||||||
data: {
|
data: {
|
||||||
identities: [{ id: "9", preferredUsername: "sam", name: "Samuel" }],
|
loggedUser: { actors: [{ id: "9", preferredUsername: "sam", name: "Samuel" }] },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,44 +1,94 @@
|
||||||
<template>
|
<template>
|
||||||
|
<div>
|
||||||
<h1 class="text-3xl">
|
<h1 class="text-3xl">
|
||||||
{{ t("Autorize this application to access your account?") }}
|
{{ t("Autorize this application to access your account?") }}
|
||||||
</h1>
|
</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="rounded-lg bg-white dark:bg-zinc-900 shadow-xl my-6">
|
||||||
<div class="p-4 pb-0">
|
<div class="p-4 pb-0">
|
||||||
<p class="text-3xl font-bold">{{ authApplication.name }}</p>
|
<p class="text-3xl font-bold">{{ authApplication.name }}</p>
|
||||||
<p>{{ authApplication.website }}</p>
|
<p>{{ authApplication.website }}</p>
|
||||||
</div>
|
</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">
|
<div class="flex gap-3 p-4">
|
||||||
<o-button @click="() => authorize()">{{ t("Authorize") }}</o-button>
|
<o-button
|
||||||
|
:disabled="collapses.length === 0"
|
||||||
|
@click="() => authorize()"
|
||||||
|
>{{ t("Authorize") }}</o-button
|
||||||
|
>
|
||||||
<o-button outlined tag="router-link" :to="{ name: RouteName.HOME }">{{
|
<o-button outlined tag="router-link" :to="{ name: RouteName.HOME }">{{
|
||||||
t("Decline")
|
t("Decline")
|
||||||
}}</o-button>
|
}}</o-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useHead } from "@vueuse/head";
|
import { useHead } from "@vueuse/head";
|
||||||
import { computed } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useMutation } from "@vue/apollo-composable";
|
import { useMutation } from "@vue/apollo-composable";
|
||||||
import { AUTORIZE_APPLICATION } from "@/graphql/application";
|
import { AUTORIZE_APPLICATION } from "@/graphql/application";
|
||||||
import AlertCircle from "vue-material-design-icons/AlertCircle.vue";
|
|
||||||
import RouteName from "@/router/name";
|
import RouteName from "@/router/name";
|
||||||
import { IApplication } from "@/types/application.model";
|
import { IApplication } from "@/types/application.model";
|
||||||
|
import { scope } from "./scope";
|
||||||
|
import AlertCircle from "vue-material-design-icons/AlertCircle.vue";
|
||||||
|
|
||||||
const { t } = useI18n({ useScope: "global" });
|
const { t } = useI18n({ useScope: "global" });
|
||||||
|
|
||||||
|
@ -49,9 +99,25 @@ const props = defineProps<{
|
||||||
scope?: string | null;
|
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 } =
|
const { mutate: authorizeMutation, onDone: onAuthorizeMutationDone } =
|
||||||
useMutation<
|
useMutation<
|
||||||
{ authorizeApplication: { code: string; state: string } },
|
{
|
||||||
|
authorizeApplication: {
|
||||||
|
code: string;
|
||||||
|
state: string;
|
||||||
|
clientId: string;
|
||||||
|
scope: string;
|
||||||
|
};
|
||||||
|
},
|
||||||
{
|
{
|
||||||
applicationClientId: string;
|
applicationClientId: string;
|
||||||
redirectURI: string;
|
redirectURI: string;
|
||||||
|
@ -71,13 +137,20 @@ const authorize = () => {
|
||||||
|
|
||||||
onAuthorizeMutationDone(({ data }) => {
|
onAuthorizeMutationDone(({ data }) => {
|
||||||
const code = data?.authorizeApplication?.code;
|
const code = data?.authorizeApplication?.code;
|
||||||
|
const localClientId = data?.authorizeApplication?.clientId;
|
||||||
|
const localScope = data?.authorizeApplication?.scope;
|
||||||
const returnedState = data?.authorizeApplication?.state ?? "";
|
const returnedState = data?.authorizeApplication?.state ?? "";
|
||||||
|
|
||||||
if (!code) return;
|
if (!code || !localClientId || !localScope) return;
|
||||||
|
|
||||||
if (props.redirectURI) {
|
if (props.redirectURI) {
|
||||||
const params = new URLSearchParams(
|
const params = new URLSearchParams(
|
||||||
Object.entries({ code, state: returnedState })
|
Object.entries({
|
||||||
|
code,
|
||||||
|
state: returnedState,
|
||||||
|
client_id: localClientId,
|
||||||
|
scope: localScope,
|
||||||
|
})
|
||||||
);
|
);
|
||||||
window.location.assign(
|
window.location.assign(
|
||||||
new URL(`${props.redirectURI}?${params.toString()}`)
|
new URL(`${props.redirectURI}?${params.toString()}`)
|
||||||
|
|
283
js/src/components/OAuth/scopes.ts
Normal file
283
js/src/components/OAuth/scopes.ts
Normal 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",
|
||||||
|
},
|
||||||
|
};
|
|
@ -250,6 +250,14 @@ const icons: Record<string, () => Promise<any>> = {
|
||||||
),
|
),
|
||||||
ExitToApp: () =>
|
ExitToApp: () =>
|
||||||
import(`../../../node_modules/vue-material-design-icons/ExitToApp.vue`),
|
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(
|
const props = withDefaults(
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
PERSON_STATUS_GROUP,
|
PERSON_STATUS_GROUP,
|
||||||
} from "@/graphql/actor";
|
} from "@/graphql/actor";
|
||||||
import { IPerson } from "@/types/actor";
|
import { IPerson } from "@/types/actor";
|
||||||
|
import { ICurrentUser } from "@/types/current-user.model";
|
||||||
import { useQuery } from "@vue/apollo-composable";
|
import { useQuery } from "@vue/apollo-composable";
|
||||||
import { computed, Ref, unref } from "vue";
|
import { computed, Ref, unref } from "vue";
|
||||||
import { useCurrentUserClient } from "./user";
|
import { useCurrentUserClient } from "./user";
|
||||||
|
@ -24,7 +25,7 @@ export function useCurrentActorClient() {
|
||||||
export function useCurrentUserIdentities() {
|
export function useCurrentUserIdentities() {
|
||||||
const { currentUser } = useCurrentUserClient();
|
const { currentUser } = useCurrentUserClient();
|
||||||
|
|
||||||
const { result, error, loading } = useQuery<{ identities: IPerson[] }>(
|
const { result, error, loading } = useQuery<{ loggedUser: Pick<ICurrentUser, 'actors'> }>(
|
||||||
IDENTITIES,
|
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 };
|
return { identities, error, loading };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,11 +82,11 @@ export function registerAccount() {
|
||||||
{ context }
|
{ context }
|
||||||
) => {
|
) => {
|
||||||
if (context?.userAlreadyActivated) {
|
if (context?.userAlreadyActivated) {
|
||||||
const identitiesData = store.readQuery<{ identities: IPerson[] }>({
|
const currentUserData = store.readQuery<{ loggedUser: Pick<ICurrentUser, 'actors'> }>({
|
||||||
query: IDENTITIES,
|
query: IDENTITIES,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (identitiesData && localData) {
|
if (currentUserData && localData) {
|
||||||
const newPersonData = {
|
const newPersonData = {
|
||||||
...localData.registerPerson,
|
...localData.registerPerson,
|
||||||
type: ActorType.PERSON,
|
type: ActorType.PERSON,
|
||||||
|
@ -95,8 +95,10 @@ export function registerAccount() {
|
||||||
store.writeQuery({
|
store.writeQuery({
|
||||||
query: IDENTITIES,
|
query: IDENTITIES,
|
||||||
data: {
|
data: {
|
||||||
...identitiesData,
|
...currentUserData.loggedUser,
|
||||||
identities: [...identitiesData.identities, newPersonData],
|
actors: [
|
||||||
|
[...currentUserData.loggedUser.actors, newPersonData]
|
||||||
|
]
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -282,13 +282,17 @@ export const LOGGED_USER_MEMBERSHIPS = gql`
|
||||||
|
|
||||||
export const IDENTITIES = gql`
|
export const IDENTITIES = gql`
|
||||||
query Identities {
|
query Identities {
|
||||||
identities {
|
loggedUser {
|
||||||
|
actors {
|
||||||
...ActorFragment
|
...ActorFragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
${ACTOR_FRAGMENT}
|
${ACTOR_FRAGMENT}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const PERSON_MEMBERSHIPS = gql`
|
export const PERSON_MEMBERSHIPS = gql`
|
||||||
query PersonMemberships($id: ID!) {
|
query PersonMemberships($id: ID!) {
|
||||||
person(id: $id) {
|
person(id: $id) {
|
||||||
|
|
|
@ -14,9 +14,9 @@ export const AUTH_APPLICATION = gql`
|
||||||
export const AUTORIZE_APPLICATION = gql`
|
export const AUTORIZE_APPLICATION = gql`
|
||||||
mutation AuthorizeApplication(
|
mutation AuthorizeApplication(
|
||||||
$applicationClientId: String!
|
$applicationClientId: String!
|
||||||
$redirectURI: String
|
$redirectURI: String!
|
||||||
$state: String
|
$state: String
|
||||||
$scope: String
|
$scope: String!
|
||||||
) {
|
) {
|
||||||
authorizeApplication(
|
authorizeApplication(
|
||||||
clientId: $applicationClientId
|
clientId: $applicationClientId
|
||||||
|
@ -26,6 +26,23 @@ export const AUTORIZE_APPLICATION = gql`
|
||||||
) {
|
) {
|
||||||
code
|
code
|
||||||
state
|
state
|
||||||
|
clientId
|
||||||
|
scope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const AUTORIZE_DEVICE_APPLICATION = gql`
|
||||||
|
mutation AuthorizeDeviceApplication(
|
||||||
|
$applicationClientId: String!
|
||||||
|
$userCode: String
|
||||||
|
) {
|
||||||
|
authorizeDeviceApplication(
|
||||||
|
clientId: $applicationClientId
|
||||||
|
userCode: $userCode
|
||||||
|
) {
|
||||||
|
clientId
|
||||||
|
scope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -72,6 +72,7 @@ export const CONFIG = gql`
|
||||||
features {
|
features {
|
||||||
groups
|
groups
|
||||||
eventCreation
|
eventCreation
|
||||||
|
antispam
|
||||||
}
|
}
|
||||||
restrictions {
|
restrictions {
|
||||||
onlyAdminCanCreateGroups
|
onlyAdminCanCreateGroups
|
||||||
|
|
|
@ -226,15 +226,6 @@ export const FETCH_GROUP = gql`
|
||||||
${RESOURCE_METADATA_BASIC_FIELDS_FRAGMENT}
|
${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`
|
export const GET_GROUP = gql`
|
||||||
query GetGroup(
|
query GetGroup(
|
||||||
$id: ID!
|
$id: ID!
|
||||||
|
@ -407,6 +398,7 @@ export const GROUP_TIMELINE = gql`
|
||||||
openness
|
openness
|
||||||
physicalAddress {
|
physicalAddress {
|
||||||
id
|
id
|
||||||
|
originId
|
||||||
}
|
}
|
||||||
banner {
|
banner {
|
||||||
id
|
id
|
||||||
|
|
|
@ -1457,5 +1457,101 @@
|
||||||
"Autorize this application to access your account?": "Autorize this application to access your account?",
|
"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.",
|
"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 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"
|
||||||
}
|
}
|
|
@ -1455,5 +1455,99 @@
|
||||||
"Autorize this application to access your account?": "Autoriser cette application à accéder à votre compte ?",
|
"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.",
|
"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 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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ export interface IApplication {
|
||||||
clientId: string;
|
clientId: string;
|
||||||
clientSecret?: string;
|
clientSecret?: string;
|
||||||
redirectUris?: string;
|
redirectUris?: string;
|
||||||
scopes: string | null;
|
scope: string | null;
|
||||||
website: string | null;
|
website: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ export interface ICurrentUser {
|
||||||
isLoggedIn: boolean;
|
isLoggedIn: boolean;
|
||||||
role: ICurrentUserRole;
|
role: ICurrentUserRole;
|
||||||
defaultActor?: IPerson;
|
defaultActor?: IPerson;
|
||||||
|
actors: IPerson[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUserPreferredLocation {
|
export interface IUserPreferredLocation {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { AUTH_USER_ACTOR_ID } from "@/constants";
|
import { AUTH_USER_ACTOR_ID } from "@/constants";
|
||||||
import { UPDATE_CURRENT_ACTOR_CLIENT, IDENTITIES } from "@/graphql/actor";
|
import { UPDATE_CURRENT_ACTOR_CLIENT, IDENTITIES } from "@/graphql/actor";
|
||||||
import { IPerson } from "@/types/actor";
|
import { IPerson } from "@/types/actor";
|
||||||
|
import { ICurrentUser } from "@/types/current-user.model";
|
||||||
import { apolloClient } from "@/vue-apollo";
|
import { apolloClient } from "@/vue-apollo";
|
||||||
import {
|
import {
|
||||||
provideApolloClient,
|
provideApolloClient,
|
||||||
|
@ -36,10 +37,10 @@ export async function initializeCurrentActor(): Promise<void> {
|
||||||
const actorId = localStorage.getItem(AUTH_USER_ACTOR_ID);
|
const actorId = localStorage.getItem(AUTH_USER_ACTOR_ID);
|
||||||
|
|
||||||
const { result: identitiesResult } = provideApolloClient(apolloClient)(() =>
|
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 () => {
|
watch(identities, async () => {
|
||||||
if (identities.value && identities.value.length < 1) {
|
if (identities.value && identities.value.length < 1) {
|
||||||
|
|
|
@ -234,6 +234,7 @@ import { convertToUsername } from "@/utils/username";
|
||||||
import { Dialog } from "@/plugins/dialog";
|
import { Dialog } from "@/plugins/dialog";
|
||||||
import { Notifier } from "@/plugins/notifier";
|
import { Notifier } from "@/plugins/notifier";
|
||||||
import { AbsintheGraphQLErrors } from "@/types/errors.model";
|
import { AbsintheGraphQLErrors } from "@/types/errors.model";
|
||||||
|
import { ICurrentUser } from "@/types/current-user.model";
|
||||||
|
|
||||||
const { t } = useI18n({ useScope: "global" });
|
const { t } = useI18n({ useScope: "global" });
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -348,7 +349,7 @@ const {
|
||||||
onError: deletePersonError,
|
onError: deletePersonError,
|
||||||
} = useMutation(DELETE_PERSON, () => ({
|
} = useMutation(DELETE_PERSON, () => ({
|
||||||
update: (store: ApolloCache<InMemoryCache>) => {
|
update: (store: ApolloCache<InMemoryCache>) => {
|
||||||
const data = store.readQuery<{ identities: IPerson[] }>({
|
const data = store.readQuery<{ loggedUser: Pick<ICurrentUser, 'actors'> }>({
|
||||||
query: IDENTITIES,
|
query: IDENTITIES,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -356,7 +357,10 @@ const {
|
||||||
store.writeQuery({
|
store.writeQuery({
|
||||||
query: IDENTITIES,
|
query: IDENTITIES,
|
||||||
data: {
|
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 client = resolveClient();
|
||||||
const data = client.readQuery<{
|
const data = client.readQuery<{
|
||||||
identities: IPerson[];
|
loggedUser: Pick<ICurrentUser, 'actors'>
|
||||||
}>({ query: IDENTITIES });
|
}>({ query: IDENTITIES });
|
||||||
if (data) {
|
if (data) {
|
||||||
await maybeUpdateCurrentActorCache(data.identities[0]);
|
await maybeUpdateCurrentActorCache(data.loggedUser.actors[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
await redirectIfNoIdentitySelected();
|
await redirectIfNoIdentitySelected();
|
||||||
|
@ -408,7 +412,7 @@ const {
|
||||||
store: ApolloCache<InMemoryCache>,
|
store: ApolloCache<InMemoryCache>,
|
||||||
{ data: updateData }: FetchResult
|
{ data: updateData }: FetchResult
|
||||||
) => {
|
) => {
|
||||||
const data = store.readQuery<{ identities: IPerson[] }>({
|
const data = store.readQuery<{ loggedUser: Pick<ICurrentUser, 'actors'> }>({
|
||||||
query: IDENTITIES,
|
query: IDENTITIES,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -452,7 +456,7 @@ const {
|
||||||
store: ApolloCache<InMemoryCache>,
|
store: ApolloCache<InMemoryCache>,
|
||||||
{ data: updateData }: FetchResult
|
{ data: updateData }: FetchResult
|
||||||
) => {
|
) => {
|
||||||
const data = store.readQuery<{ identities: IPerson[] }>({
|
const data = store.readQuery<{ loggedUser: Pick<ICurrentUser, 'actors'> }>({
|
||||||
query: IDENTITIES,
|
query: IDENTITIES,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -460,10 +464,13 @@ const {
|
||||||
store.writeQuery({
|
store.writeQuery({
|
||||||
query: IDENTITIES,
|
query: IDENTITIES,
|
||||||
data: {
|
data: {
|
||||||
identities: [
|
loggedUser: {
|
||||||
...data.identities,
|
...data.loggedUser,
|
||||||
|
actors: [
|
||||||
|
...data.loggedUser.actors,
|
||||||
{ ...updateData?.createPerson, type: ActorType.PERSON },
|
{ ...updateData?.createPerson, type: ActorType.PERSON },
|
||||||
],
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,27 +18,47 @@
|
||||||
|
|
||||||
<section class="timeline">
|
<section class="timeline">
|
||||||
<o-field>
|
<o-field>
|
||||||
<o-radio v-model="activityType" :native-value="undefined">
|
<o-radio class="pr-4" v-model="activityType" :native-value="undefined">
|
||||||
<TimelineText />
|
<TimelineText />
|
||||||
{{ t("All activities") }}</o-radio
|
{{ 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>
|
<o-icon icon="account-multiple-plus"></o-icon>
|
||||||
{{ t("Members") }}</o-radio
|
{{ 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>
|
<o-icon icon="cog"></o-icon>
|
||||||
{{ t("Settings") }}</o-radio
|
{{ 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>
|
<o-icon icon="calendar"></o-icon>
|
||||||
{{ t("Events") }}</o-radio
|
{{ 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>
|
<o-icon icon="bullhorn"></o-icon>
|
||||||
{{ t("Posts") }}</o-radio
|
{{ 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>
|
<o-icon icon="chat"></o-icon>
|
||||||
{{ t("Discussions") }}</o-radio
|
{{ t("Discussions") }}</o-radio
|
||||||
>
|
>
|
||||||
|
@ -48,11 +68,16 @@
|
||||||
>
|
>
|
||||||
</o-field>
|
</o-field>
|
||||||
<o-field>
|
<o-field>
|
||||||
<o-radio v-model="activityAuthor" :native-value="undefined">
|
<o-radio
|
||||||
|
class="pr-4"
|
||||||
|
v-model="activityAuthor"
|
||||||
|
:native-value="undefined"
|
||||||
|
>
|
||||||
<TimelineText />
|
<TimelineText />
|
||||||
{{ t("All activities") }}</o-radio
|
{{ t("All activities") }}</o-radio
|
||||||
>
|
>
|
||||||
<o-radio
|
<o-radio
|
||||||
|
class="pr-4"
|
||||||
v-model="activityAuthor"
|
v-model="activityAuthor"
|
||||||
:native-value="ActivityAuthorFilter.SELF"
|
:native-value="ActivityAuthorFilter.SELF"
|
||||||
>
|
>
|
||||||
|
@ -60,6 +85,7 @@
|
||||||
{{ t("From yourself") }}</o-radio
|
{{ t("From yourself") }}</o-radio
|
||||||
>
|
>
|
||||||
<o-radio
|
<o-radio
|
||||||
|
class="pr-4"
|
||||||
v-model="activityAuthor"
|
v-model="activityAuthor"
|
||||||
:native-value="ActivityAuthorFilter.BY"
|
:native-value="ActivityAuthorFilter.BY"
|
||||||
>
|
>
|
||||||
|
@ -89,7 +115,7 @@
|
||||||
<h2 v-else>
|
<h2 v-else>
|
||||||
{{ formatDateString(date) }}
|
{{ formatDateString(date) }}
|
||||||
</h2>
|
</h2>
|
||||||
<ul>
|
<ul class="before:opacity-10">
|
||||||
<li v-for="activityItem in activityItems" :key="activityItem.id">
|
<li v-for="activityItem in activityItems" :key="activityItem.id">
|
||||||
<skeleton-activity-item v-if="activityItem.type === 'skeleton'" />
|
<skeleton-activity-item v-if="activityItem.type === 'skeleton'" />
|
||||||
<component
|
<component
|
||||||
|
@ -202,6 +228,7 @@ const page = ref(1);
|
||||||
const {
|
const {
|
||||||
result: groupTimelineResult,
|
result: groupTimelineResult,
|
||||||
fetchMore: fetchMoreActivities,
|
fetchMore: fetchMoreActivities,
|
||||||
|
onError: onGroupTLError,
|
||||||
loading,
|
loading,
|
||||||
} = useQuery<{ group: IGroup }>(GROUP_TIMELINE, () => ({
|
} = useQuery<{ group: IGroup }>(GROUP_TIMELINE, () => ({
|
||||||
preferredUsername: props.preferredUsername,
|
preferredUsername: props.preferredUsername,
|
||||||
|
@ -211,6 +238,8 @@ const {
|
||||||
author: activityAuthor.value,
|
author: activityAuthor.value,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
onGroupTLError((err) => console.error(err));
|
||||||
|
|
||||||
const group = computed(() => groupTimelineResult.value?.group);
|
const group = computed(() => groupTimelineResult.value?.group);
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
|
|
|
@ -36,8 +36,8 @@
|
||||||
/>
|
/>
|
||||||
<div v-show="authApplicationError">
|
<div v-show="authApplicationError">
|
||||||
<div
|
<div
|
||||||
class="rounded-lg bg-mbz-danger shadow-xl my-6 p-4 flex items-center gap-2"
|
class="rounded-lg text-white bg-mbz-danger shadow-xl my-6 p-4 flex items-center gap-2"
|
||||||
v-if="authApplicationGraphError?.status_code === 404"
|
v-if="authApplicationGraphError?.message === 'not_found'"
|
||||||
>
|
>
|
||||||
<AlertCircle :size="42" />
|
<AlertCircle :size="42" />
|
||||||
<div>
|
<div>
|
||||||
|
@ -72,9 +72,6 @@
|
||||||
<p class="text-4xl">{{ resultCode }}</p>
|
<p class="text-4xl">{{ resultCode }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<o-button variant="text" tag="router-link" :to="{ name: RouteName.HOME }">{{
|
|
||||||
t("Back to homepage")
|
|
||||||
}}</o-button>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -83,8 +80,8 @@ import { useRouteQuery } from "vue-use-route-query";
|
||||||
import { useHead } from "@vueuse/head";
|
import { useHead } from "@vueuse/head";
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useMutation, useQuery } from "@vue/apollo-composable";
|
import { useQuery } from "@vue/apollo-composable";
|
||||||
import { AUTH_APPLICATION, AUTORIZE_APPLICATION } from "@/graphql/application";
|
import { AUTH_APPLICATION } from "@/graphql/application";
|
||||||
import { IApplication } from "@/types/application.model";
|
import { IApplication } from "@/types/application.model";
|
||||||
import AlertCircle from "vue-material-design-icons/AlertCircle.vue";
|
import AlertCircle from "vue-material-design-icons/AlertCircle.vue";
|
||||||
import type { AbsintheGraphQLError } from "@/types/errors.model";
|
import type { AbsintheGraphQLError } from "@/types/errors.model";
|
||||||
|
@ -98,7 +95,6 @@ const redirectURI = useRouteQuery("redirect_uri", null);
|
||||||
const state = useRouteQuery("state", null);
|
const state = useRouteQuery("state", null);
|
||||||
const scope = useRouteQuery("scope", 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 resultCode = ref<string | null>(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -123,44 +119,6 @@ const authApplicationGraphError = computed(
|
||||||
() => authApplicationError.value?.graphQLErrors[0] as AbsintheGraphQLError
|
() => 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({
|
useHead({
|
||||||
title: computed(() => t("Authorize application")),
|
title: computed(() => t("Authorize application")),
|
||||||
});
|
});
|
||||||
|
|
|
@ -136,10 +136,8 @@ const { mutate: revoke, onDone: onRevokedApplication } = useMutation<
|
||||||
const notifier = inject<Notifier>("notifier");
|
const notifier = inject<Notifier>("notifier");
|
||||||
|
|
||||||
onRevokedApplication(() => {
|
onRevokedApplication(() => {
|
||||||
notifier?.success(
|
notifier?.success(t("Application was revoked"));
|
||||||
t("Application was revoked")
|
});
|
||||||
);
|
|
||||||
})
|
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: computed(() => t("Apps")),
|
title: computed(() => t("Apps")),
|
||||||
|
|
|
@ -45,6 +45,13 @@ export default defineConfig(({ command }) => {
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"@": path.resolve(__dirname, "./src"),
|
"@": path.resolve(__dirname, "./src"),
|
||||||
|
unfetch: path.resolve(
|
||||||
|
__dirname,
|
||||||
|
"node_modules",
|
||||||
|
"unfetch",
|
||||||
|
"dist",
|
||||||
|
"unfetch.mjs"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
css: {
|
css: {
|
||||||
|
|
1818
js/yarn.lock
1818
js/yarn.lock
File diff suppressed because it is too large
Load diff
86
lib/graphql/authorization.ex
Normal file
86
lib/graphql/authorization.ex
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
defmodule Mobilizon.GraphQL.Authorization do
|
||||||
|
@moduledoc """
|
||||||
|
Check authorizations
|
||||||
|
"""
|
||||||
|
|
||||||
|
use Rajska,
|
||||||
|
valid_roles: [:user, :moderator, :administrator],
|
||||||
|
super_role: :administrator,
|
||||||
|
default_rule: :default
|
||||||
|
|
||||||
|
alias Mobilizon.Applications.ApplicationToken
|
||||||
|
alias Mobilizon.GraphQL.Authorization.AppScope
|
||||||
|
alias Mobilizon.Users.User
|
||||||
|
import Mobilizon.Web.Gettext, only: [dgettext: 3]
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def has_user_access?(%User{}, _scope, _rule), do: true
|
||||||
|
|
||||||
|
def has_user_access?(%ApplicationToken{scope: scope} = _current_app_token, _struct, rule)
|
||||||
|
when rule != :forbid_app_access do
|
||||||
|
AppScope.has_app_access?(scope, rule)
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_user_access?(_current_user, _scoped_struct, _rule), do: false
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def get_current_user(%{current_auth_app_token: app_token}), do: app_token
|
||||||
|
def get_current_user(%{current_user: current_user}), do: current_user
|
||||||
|
def get_current_user(_ctx), do: nil
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def role_authorized?(_user_role, :all), do: true
|
||||||
|
def role_authorized?(role, _allowed_role) when is_super_role(role), do: true
|
||||||
|
|
||||||
|
def role_authorized?(user_role, allowed_role) when is_atom(user_role) and is_atom(allowed_role),
|
||||||
|
do: user_role === allowed_role
|
||||||
|
|
||||||
|
def role_authorized?(user_role, allowed_roles)
|
||||||
|
when is_atom(user_role) and is_list(allowed_roles),
|
||||||
|
do: user_role in allowed_roles
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def get_user_role(%ApplicationToken{user: %{role: role}}), do: role
|
||||||
|
def get_user_role(%{role: role}), do: role
|
||||||
|
def get_user_role(nil), do: nil
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def unauthorized_message(resolution) do
|
||||||
|
case Map.get(resolution.context, :current_user) do
|
||||||
|
nil ->
|
||||||
|
"unauthenticated"
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
"unauthorized"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def unauthorized_query_scope_message(_resolution, object_type) do
|
||||||
|
dgettext("errors", "Not authorized to access this %{object_type}",
|
||||||
|
object_type: replace_underscore(object_type)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def unauthorized_object_scope_message(_result_object, object) do
|
||||||
|
dgettext("errors", "Not authorized to access object %{object}", object: object.identifier)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def unauthorized_object_message(_resolution, object) do
|
||||||
|
dgettext("errors", "Not authorized to access object %{object}", object: object.identifier)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def unauthorized_field_message(_resolution, field),
|
||||||
|
do: dgettext("errors", "Not authorized to access field %{field}", field: field)
|
||||||
|
|
||||||
|
defp replace_underscore(string) when is_binary(string), do: String.replace(string, "_", " ")
|
||||||
|
|
||||||
|
defp replace_underscore(atom) when is_atom(atom) do
|
||||||
|
atom
|
||||||
|
|> Atom.to_string()
|
||||||
|
|> replace_underscore()
|
||||||
|
end
|
||||||
|
end
|
118
lib/graphql/authorization/app_scope.ex
Normal file
118
lib/graphql/authorization/app_scope.ex
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
defmodule Mobilizon.GraphQL.Authorization.AppScope do
|
||||||
|
@moduledoc """
|
||||||
|
Module referencing all scopes usable in the Mobilizon API
|
||||||
|
"""
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
@global_scope %{
|
||||||
|
"write" => [
|
||||||
|
# Media
|
||||||
|
:"write:media:upload",
|
||||||
|
:"write:media:remove",
|
||||||
|
# Event permissions
|
||||||
|
:"write:event:create",
|
||||||
|
:"write:event:update",
|
||||||
|
:"write:event:delete",
|
||||||
|
# Comment permissions
|
||||||
|
:"write:comment:create",
|
||||||
|
:"write:comment:update",
|
||||||
|
:"write:comment:delete",
|
||||||
|
# Event participation permission
|
||||||
|
:"write:participation",
|
||||||
|
# User account permissions
|
||||||
|
:"write:user:settings",
|
||||||
|
:"write:user:setting:activity",
|
||||||
|
:"write:user:setting:push",
|
||||||
|
# Profile permissions
|
||||||
|
:"write:profile:create",
|
||||||
|
:"write:profile:update",
|
||||||
|
:"write:profile:delete",
|
||||||
|
:"write:profile:feed_token:create",
|
||||||
|
:"write:feed_token:delete",
|
||||||
|
# Membership permissions
|
||||||
|
:"write:group_membership",
|
||||||
|
# Group permissions
|
||||||
|
:"write:group:create",
|
||||||
|
:"write:group:update",
|
||||||
|
:"write:group:delete",
|
||||||
|
# Group discussions permissions
|
||||||
|
:"write:group:discussion:create",
|
||||||
|
:"write:group:discussion:update",
|
||||||
|
:"write:group:discussion:delete",
|
||||||
|
# Group resources permissions
|
||||||
|
:"write:group:resources:create",
|
||||||
|
:"write:group:resources:update",
|
||||||
|
:"write:group:resources:delete",
|
||||||
|
# Group members
|
||||||
|
:"write:group:members",
|
||||||
|
# Post permissions
|
||||||
|
:"write:group:post:create",
|
||||||
|
:"write:group:post:update",
|
||||||
|
:"write:group:post:delete"
|
||||||
|
],
|
||||||
|
"read" => [
|
||||||
|
:"read:event",
|
||||||
|
:"read:event:participants",
|
||||||
|
:"read:event:participants:export",
|
||||||
|
:"read:user:settings",
|
||||||
|
# Profile permissions
|
||||||
|
:"read:profile",
|
||||||
|
:"read:profile:organized_events",
|
||||||
|
:"read:profile:participations",
|
||||||
|
:"read:profile:memberships",
|
||||||
|
:"read:profile:follows",
|
||||||
|
# Group details permissions
|
||||||
|
:"read:group",
|
||||||
|
:"read:group:events",
|
||||||
|
:"read:group:discussions",
|
||||||
|
:"read:group:resources",
|
||||||
|
:"read:group:members",
|
||||||
|
:"read:group:followers",
|
||||||
|
:"read:group:todo_lists",
|
||||||
|
:"read:group:activities"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@spec get_scopes :: list(atom())
|
||||||
|
def get_scopes do
|
||||||
|
@global_scope
|
||||||
|
|> Map.values()
|
||||||
|
|> Enum.concat()
|
||||||
|
|> Enum.concat([:read, :write])
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec scopes_valid?(String.t()) :: boolean()
|
||||||
|
def scopes_valid?(scopes) do
|
||||||
|
scopes
|
||||||
|
|> String.split(" ")
|
||||||
|
|> Enum.all?(&scope_valid?/1)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec scope_valid?(String.t() | atom()) :: boolean()
|
||||||
|
def scope_valid?(scope) when is_binary(scope) do
|
||||||
|
scope in Enum.map(get_scopes(), &to_string/1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def scope_valid?(scope) when is_atom(scope) do
|
||||||
|
scope in get_scopes()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec has_app_access?(binary, atom | binary) :: boolean
|
||||||
|
def has_app_access?(scope, rule) do
|
||||||
|
Logger.debug("Has app token access? scope: #{inspect(scope)}, rule: #{inspect(rule)}")
|
||||||
|
scope = String.split(scope, " ")
|
||||||
|
scope_acceptable_for_rule?(scope, rule) or global_scopes_acceptable_for_rule?(scope, rule)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec scope_acceptable_for_rule?(list(String.t() | atom()), String.t() | atom()) :: boolean()
|
||||||
|
defp scope_acceptable_for_rule?(scope, rule) when is_list(scope) do
|
||||||
|
to_string(rule) in Enum.map(scope, &to_string/1)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp global_scopes_acceptable_for_rule?(scope, rule),
|
||||||
|
do: Enum.any?(scope, &global_scope_acceptable_for_rule?(&1, rule))
|
||||||
|
|
||||||
|
defp global_scope_acceptable_for_rule?(global_scope, rule),
|
||||||
|
do: scope_acceptable_for_rule?(Map.get(@global_scope, global_scope, []), rule)
|
||||||
|
end
|
|
@ -30,6 +30,17 @@ defmodule Mobilizon.GraphQL.Error do
|
||||||
handle(reason)
|
handle(reason)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# It's unclear why returned errors are now binaries instead of atoms
|
||||||
|
# but we can still convert them back
|
||||||
|
def normalize(string) when is_binary(string) do
|
||||||
|
string
|
||||||
|
|> String.to_existing_atom()
|
||||||
|
|> handle()
|
||||||
|
rescue
|
||||||
|
ArgumentError ->
|
||||||
|
handle(string)
|
||||||
|
end
|
||||||
|
|
||||||
# Unhandled errors
|
# Unhandled errors
|
||||||
def normalize(other) do
|
def normalize(other) do
|
||||||
handle(other)
|
handle(other)
|
||||||
|
@ -65,6 +76,9 @@ defmodule Mobilizon.GraphQL.Error do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle(reason) when is_binary(reason) do
|
defp handle(reason) when is_binary(reason) do
|
||||||
|
Logger.debug("Unknown error")
|
||||||
|
Logger.debug(reason)
|
||||||
|
|
||||||
%Error{
|
%Error{
|
||||||
code: :unknown_error,
|
code: :unknown_error,
|
||||||
message: reason,
|
message: reason,
|
||||||
|
@ -101,6 +115,11 @@ defmodule Mobilizon.GraphQL.Error do
|
||||||
defp metadata(:group_not_found), do: {404, dgettext("errors", "Group not found")}
|
defp metadata(:group_not_found), do: {404, dgettext("errors", "Group not found")}
|
||||||
defp metadata(:resource_not_found), do: {404, dgettext("errors", "Resource not found")}
|
defp metadata(:resource_not_found), do: {404, dgettext("errors", "Resource not found")}
|
||||||
defp metadata(:discussion_not_found), do: {404, dgettext("errors", "Discussion not found")}
|
defp metadata(:discussion_not_found), do: {404, dgettext("errors", "Discussion not found")}
|
||||||
|
defp metadata(:application_not_found), do: {404, dgettext("errors", "Application not found")}
|
||||||
|
|
||||||
|
defp metadata(:application_token_not_found),
|
||||||
|
do: {404, dgettext("errors", "Application token not found")}
|
||||||
|
|
||||||
defp metadata(:unknown), do: {500, dgettext("errors", "Something went wrong")}
|
defp metadata(:unknown), do: {500, dgettext("errors", "Something went wrong")}
|
||||||
|
|
||||||
defp metadata(code) do
|
defp metadata(code) do
|
||||||
|
|
|
@ -12,7 +12,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Application do
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Create an application
|
Authorize an application
|
||||||
"""
|
"""
|
||||||
@spec authorize(any(), map(), Absinthe.Resolution.t()) :: {:ok, map()} | {:error, String.t()}
|
@spec authorize(any(), map(), Absinthe.Resolution.t()) :: {:ok, map()} | {:error, String.t()}
|
||||||
def authorize(
|
def authorize(
|
||||||
|
@ -21,8 +21,16 @@ defmodule Mobilizon.GraphQL.Resolvers.Application do
|
||||||
%{context: %{current_user: %User{id: user_id}}}
|
%{context: %{current_user: %User{id: user_id}}}
|
||||||
) do
|
) do
|
||||||
case Applications.autorize(client_id, redirect_uri, scope, user_id) do
|
case Applications.autorize(client_id, redirect_uri, scope, user_id) do
|
||||||
{:ok, code} ->
|
{:ok,
|
||||||
{:ok, %{code: code, state: state}}
|
%ApplicationToken{
|
||||||
|
application: %Application{client_id: client_id},
|
||||||
|
scope: scope,
|
||||||
|
authorization_code: code
|
||||||
|
}} ->
|
||||||
|
{:ok, %{code: code, state: state, client_id: client_id, scope: scope}}
|
||||||
|
|
||||||
|
{:error, %Ecto.Changeset{} = err} ->
|
||||||
|
{:error, err}
|
||||||
|
|
||||||
{:error, :application_not_found} ->
|
{:error, :application_not_found} ->
|
||||||
{:error,
|
{:error,
|
||||||
|
@ -41,18 +49,18 @@ defmodule Mobilizon.GraphQL.Resolvers.Application do
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorize(_parent, _args, _context) do
|
def authorize(_parent, _args, _context) do
|
||||||
{:error, dgettext("errors", "You need to be logged-in to autorize applications")}
|
{:error, :unauthenticated}
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_application(any(), map(), Absinthe.Resolution.t()) ::
|
@spec get_application(any(), map(), Absinthe.Resolution.t()) ::
|
||||||
{:ok, Application.t()} | {:error, :not_found | :unauthenticated}
|
{:ok, Application.t()} | {:error, :application_not_found | :unauthenticated}
|
||||||
def get_application(_parent, %{client_id: client_id}, %{context: %{current_user: %User{}}}) do
|
def get_application(_parent, %{client_id: client_id}, %{context: %{current_user: %User{}}}) do
|
||||||
case ApplicationManager.get_application_by_client_id(client_id) do
|
case ApplicationManager.get_application_by_client_id(client_id) do
|
||||||
%Application{} = application ->
|
%Application{} = application ->
|
||||||
{:ok, application}
|
{:ok, application}
|
||||||
|
|
||||||
nil ->
|
nil ->
|
||||||
{:error, :not_found}
|
{:error, :application_not_found}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -82,7 +90,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Application do
|
||||||
end
|
end
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
{:error, :not_found}
|
{:error, :application_token_not_found}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -93,29 +101,53 @@ defmodule Mobilizon.GraphQL.Resolvers.Application do
|
||||||
def activate_device(_parent, %{user_code: user_code}, %{
|
def activate_device(_parent, %{user_code: user_code}, %{
|
||||||
context: %{current_user: %User{} = user}
|
context: %{current_user: %User{} = user}
|
||||||
}) do
|
}) do
|
||||||
with {:ok, %ApplicationDeviceActivation{} = app_device_activation} <-
|
case Applications.activate_device(user_code, user) do
|
||||||
Applications.activate_device(user_code, user) do
|
{:ok, %ApplicationDeviceActivation{} = app_device_activation} ->
|
||||||
{:ok, app_device_activation |> Map.from_struct() |> Map.take([:application, :id, :scope])}
|
{:ok, app_device_activation |> Map.from_struct() |> Map.take([:application, :id, :scope])}
|
||||||
|
|
||||||
|
{:error, :expired} ->
|
||||||
|
{:error, dgettext("errors", "The given user code has expired")}
|
||||||
|
|
||||||
|
{:error, :not_found} ->
|
||||||
|
{:error, dgettext("errors", "The given user code is invalid")}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def activate_device(_parent, _args, _resolution) do
|
||||||
|
{:error, :unauthenticated}
|
||||||
|
end
|
||||||
|
|
||||||
@spec authorize_device_application(any(), map(), Absinthe.Resolution.t()) ::
|
@spec authorize_device_application(any(), map(), Absinthe.Resolution.t()) ::
|
||||||
{:ok, map()} | {:error, String.t()}
|
{:ok, map()} | {:error, String.t()}
|
||||||
def authorize_device_application(
|
def authorize_device_application(
|
||||||
_parent,
|
_parent,
|
||||||
%{client_id: client_id, user_code: user_code},
|
%{client_id: client_id, user_code: user_code},
|
||||||
%{context: %{current_user: %User{id: user_id}}}
|
%{context: %{current_user: %User{}}}
|
||||||
) do
|
) do
|
||||||
case Applications.autorize_device_application(client_id, user_code, user_id) do
|
case Applications.autorize_device_application(client_id, user_code) do
|
||||||
{:ok, %Application{} = app} ->
|
{:ok, %ApplicationDeviceActivation{application: app}} ->
|
||||||
{:ok, app}
|
{:ok, app}
|
||||||
|
|
||||||
{:error, :application_not_found} ->
|
{:error, :not_confirmed} ->
|
||||||
{:error,
|
{:error,
|
||||||
dgettext(
|
dgettext(
|
||||||
"errors",
|
"errors",
|
||||||
"No application with this client_id was found"
|
"The device user code was not provided before approving the application"
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{:error, :not_found} ->
|
||||||
|
{:error,
|
||||||
|
dgettext(
|
||||||
|
"errors",
|
||||||
|
"The given user code is invalid"
|
||||||
|
)}
|
||||||
|
|
||||||
|
{:error, :expired} ->
|
||||||
|
{:error, dgettext("errors", "The given user code has expired")}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def authorize_device_application(_parent, _args, _resolution) do
|
||||||
|
{:error, :unauthenticated}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -44,7 +44,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Media do
|
||||||
def upload_media(
|
def upload_media(
|
||||||
_parent,
|
_parent,
|
||||||
%{file: %Plug.Upload{} = file} = args,
|
%{file: %Plug.Upload{} = file} = args,
|
||||||
%{context: %{current_actor: %Actor{id: actor_id}}}
|
%{context: %{current_actor: %Actor{id: default_actor_id}}}
|
||||||
) do
|
) do
|
||||||
with {:ok,
|
with {:ok,
|
||||||
%{
|
%{
|
||||||
|
@ -62,7 +62,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Media do
|
||||||
{:ok, media = %Media{}} <-
|
{:ok, media = %Media{}} <-
|
||||||
Medias.create_media(%{
|
Medias.create_media(%{
|
||||||
file: args,
|
file: args,
|
||||||
actor_id: actor_id,
|
actor_id: Map.get(args, :actor_id, default_actor_id),
|
||||||
metadata: Map.take(uploaded, [:width, :height, :blurhash])
|
metadata: Map.take(uploaded, [:width, :height, :blurhash])
|
||||||
}) do
|
}) do
|
||||||
{:ok, transform_media(media)}
|
{:ok, transform_media(media)}
|
||||||
|
|
|
@ -20,8 +20,8 @@ defmodule Mobilizon.GraphQL.Schema do
|
||||||
alias Mobilizon.Actors.{Actor, Follower, Member}
|
alias Mobilizon.Actors.{Actor, Follower, Member}
|
||||||
alias Mobilizon.Discussions.Comment
|
alias Mobilizon.Discussions.Comment
|
||||||
alias Mobilizon.Events.{Event, Participant}
|
alias Mobilizon.Events.{Event, Participant}
|
||||||
|
alias Mobilizon.GraphQL.{Authorization, Schema}
|
||||||
alias Mobilizon.GraphQL.Middleware.{CurrentActorProvider, ErrorHandler, OperationNameLogger}
|
alias Mobilizon.GraphQL.Middleware.{CurrentActorProvider, ErrorHandler, OperationNameLogger}
|
||||||
alias Mobilizon.GraphQL.Schema
|
|
||||||
alias Mobilizon.GraphQL.Schema.Custom
|
alias Mobilizon.GraphQL.Schema.Custom
|
||||||
alias Mobilizon.Storage.Repo
|
alias Mobilizon.Storage.Repo
|
||||||
|
|
||||||
|
@ -57,11 +57,13 @@ defmodule Mobilizon.GraphQL.Schema do
|
||||||
|
|
||||||
@desc "A struct containing the id of the deleted object"
|
@desc "A struct containing the id of the deleted object"
|
||||||
object :deleted_object do
|
object :deleted_object do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:id, :id)
|
field(:id, :id)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "A JWT and the associated user ID"
|
@desc "A JWT and the associated user ID"
|
||||||
object :login do
|
object :login do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:access_token, non_null(:string), description: "A JWT Token for this session")
|
field(:access_token, non_null(:string), description: "A JWT Token for this session")
|
||||||
|
|
||||||
field(:refresh_token, non_null(:string),
|
field(:refresh_token, non_null(:string),
|
||||||
|
@ -75,6 +77,7 @@ defmodule Mobilizon.GraphQL.Schema do
|
||||||
Represents a notification for an user
|
Represents a notification for an user
|
||||||
"""
|
"""
|
||||||
object :notification do
|
object :notification do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:id, :id, description: "The notification ID")
|
field(:id, :id, description: "The notification ID")
|
||||||
field(:user, :user, description: "The user to transmit the notification to")
|
field(:user, :user, description: "The user to transmit the notification to")
|
||||||
field(:actor, :actor, description: "The notification target profile")
|
field(:actor, :actor, description: "The notification target profile")
|
||||||
|
@ -133,7 +136,9 @@ defmodule Mobilizon.GraphQL.Schema do
|
||||||
|> Dataloader.add_source(Resources, default_source)
|
|> Dataloader.add_source(Resources, default_source)
|
||||||
|> Dataloader.add_source(Todos, default_source)
|
|> Dataloader.add_source(Todos, default_source)
|
||||||
|
|
||||||
Map.put(ctx, :loader, loader)
|
ctx
|
||||||
|
|> Map.put(:loader, loader)
|
||||||
|
|> Map.put(:authorization, Authorization)
|
||||||
end
|
end
|
||||||
|
|
||||||
def plugins do
|
def plugins do
|
||||||
|
@ -201,11 +206,19 @@ defmodule Mobilizon.GraphQL.Schema do
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec middleware(list(module()), any(), map()) :: list(module())
|
@spec middleware(list(module()), any(), map()) :: list(module())
|
||||||
def middleware(middleware, _field, %{identifier: type}) when type in [:query, :mutation] do
|
def middleware(middleware, field, %{identifier: type}) when type in [:query, :mutation] do
|
||||||
[CurrentActorProvider] ++ middleware ++ [ErrorHandler, OperationNameLogger]
|
[CurrentActorProvider | middleware]
|
||||||
|
|> Enum.map(&fix_middleware_format_for_rajska/1)
|
||||||
|
|> Rajska.add_query_authorization(field, Authorization)
|
||||||
|
|> Rajska.add_object_authorization()
|
||||||
|
|> List.insert_at(-1, ErrorHandler)
|
||||||
|
|> List.insert_at(-1, OperationNameLogger)
|
||||||
end
|
end
|
||||||
|
|
||||||
def middleware(middleware, _field, _object) do
|
def middleware(middleware, field, object) do
|
||||||
middleware
|
Rajska.add_field_authorization(middleware, field, object)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp fix_middleware_format_for_rajska({mod, config}), do: {mod, config}
|
||||||
|
defp fix_middleware_format_for_rajska(mod), do: {mod, nil}
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,11 +25,13 @@ defmodule Mobilizon.GraphQL.Schema.ActivityType do
|
||||||
end
|
end
|
||||||
|
|
||||||
object :activity_param_item do
|
object :activity_param_item do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:key, :string)
|
field(:key, :string)
|
||||||
field(:value, :string)
|
field(:value, :string)
|
||||||
end
|
end
|
||||||
|
|
||||||
interface :activity_object do
|
interface :activity_object do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:id, :id)
|
field(:id, :id)
|
||||||
|
|
||||||
resolve_type(fn
|
resolve_type(fn
|
||||||
|
@ -66,11 +68,13 @@ defmodule Mobilizon.GraphQL.Schema.ActivityType do
|
||||||
A paginated list of activity items
|
A paginated list of activity items
|
||||||
"""
|
"""
|
||||||
object :paginated_activity_list do
|
object :paginated_activity_list do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:elements, list_of(:activity), description: "A list of activities")
|
field(:elements, list_of(:activity), description: "A list of activities")
|
||||||
field(:total, :integer, description: "The total number of elements in the list")
|
field(:total, :integer, description: "The total number of elements in the list")
|
||||||
end
|
end
|
||||||
|
|
||||||
object :activity do
|
object :activity do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:id, :id, description: "The activity item ID")
|
field(:id, :id, description: "The activity item ID")
|
||||||
field(:inserted_at, :datetime, description: "When was the activity inserted")
|
field(:inserted_at, :datetime, description: "When was the activity inserted")
|
||||||
field(:priority, :integer)
|
field(:priority, :integer)
|
||||||
|
|
|
@ -13,6 +13,7 @@ defmodule Mobilizon.GraphQL.Schema.ActorInterface do
|
||||||
|
|
||||||
@desc "An ActivityPub actor"
|
@desc "An ActivityPub actor"
|
||||||
interface :actor do
|
interface :actor do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:id, :id, description: "Internal ID for this actor")
|
field(:id, :id, description: "Internal ID for this actor")
|
||||||
field(:url, :string, description: "The ActivityPub actor's URL")
|
field(:url, :string, description: "The ActivityPub actor's URL")
|
||||||
field(:type, :actor_type, description: "The type of Actor (Person, Group,…)")
|
field(:type, :actor_type, description: "The type of Actor (Person, Group,…)")
|
||||||
|
@ -65,18 +66,21 @@ defmodule Mobilizon.GraphQL.Schema.ActorInterface do
|
||||||
@desc "Suspend an actor"
|
@desc "Suspend an actor"
|
||||||
field :suspend_profile, :deleted_object do
|
field :suspend_profile, :deleted_object do
|
||||||
arg(:id, non_null(:id), description: "The remote profile ID to suspend")
|
arg(:id, non_null(:id), description: "The remote profile ID to suspend")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :moderator, scope: false)
|
||||||
resolve(&ActorResolver.suspend_profile/3)
|
resolve(&ActorResolver.suspend_profile/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Unsuspend an actor"
|
@desc "Unsuspend an actor"
|
||||||
field :unsuspend_profile, :actor do
|
field :unsuspend_profile, :actor do
|
||||||
arg(:id, non_null(:id), description: "The remote profile ID to unsuspend")
|
arg(:id, non_null(:id), description: "The remote profile ID to unsuspend")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :moderator, scope: false)
|
||||||
resolve(&ActorResolver.unsuspend_profile/3)
|
resolve(&ActorResolver.unsuspend_profile/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Refresh a profile"
|
@desc "Refresh a profile"
|
||||||
field :refresh_profile, :actor do
|
field :refresh_profile, :actor do
|
||||||
arg(:id, non_null(:id), description: "The remote profile ID to refresh")
|
arg(:id, non_null(:id), description: "The remote profile ID to refresh")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :moderator, scope: false)
|
||||||
resolve(&ActorResolver.refresh_profile/3)
|
resolve(&ActorResolver.refresh_profile/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,6 +10,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.ApplicationType do
|
||||||
Represents an application
|
Represents an application
|
||||||
"""
|
"""
|
||||||
object :application do
|
object :application do
|
||||||
|
meta(:authorize, :all)
|
||||||
interfaces([:actor])
|
interfaces([:actor])
|
||||||
|
|
||||||
field(:id, :id, description: "Internal ID for this application")
|
field(:id, :id, description: "Internal ID for this application")
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.FollowerType do
|
||||||
Represents an actor's follower
|
Represents an actor's follower
|
||||||
"""
|
"""
|
||||||
object :follower do
|
object :follower do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:id, :id, description: "The follow ID")
|
field(:id, :id, description: "The follow ID")
|
||||||
field(:target_actor, :actor, description: "What or who the profile follows")
|
field(:target_actor, :actor, description: "What or who the profile follows")
|
||||||
field(:actor, :actor, description: "Which profile follows")
|
field(:actor, :actor, description: "Which profile follows")
|
||||||
|
@ -30,6 +31,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.FollowerType do
|
||||||
A paginated list of follower objects
|
A paginated list of follower objects
|
||||||
"""
|
"""
|
||||||
object :paginated_follower_list do
|
object :paginated_follower_list do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:elements, list_of(:follower), description: "A list of followers")
|
field(:elements, list_of(:follower), description: "A list of followers")
|
||||||
field(:total, :integer, description: "The total number of elements in the list")
|
field(:total, :integer, description: "The total number of elements in the list")
|
||||||
end
|
end
|
||||||
|
@ -43,6 +45,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.FollowerType do
|
||||||
description: "Whether the follower has been approved by the target actor or not"
|
description: "Whether the follower has been approved by the target actor or not"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
|
|
||||||
resolve(&Followers.update_follower/3)
|
resolve(&Followers.update_follower/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,6 +29,9 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
Represents a group of actors
|
Represents a group of actors
|
||||||
"""
|
"""
|
||||||
object :group do
|
object :group do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
meta(:scope_field?, true)
|
||||||
|
|
||||||
interfaces([:actor, :interactable, :activity_object, :action_log_object, :group_search_result])
|
interfaces([:actor, :interactable, :activity_object, :action_log_object, :group_search_result])
|
||||||
|
|
||||||
field(:id, :id, description: "Internal ID for this group")
|
field(:id, :id, description: "Internal ID for this group")
|
||||||
|
@ -77,7 +80,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
)
|
)
|
||||||
|
|
||||||
# This one should have a privacy setting
|
# This one should have a privacy setting
|
||||||
field :organized_events, :paginated_event_list do
|
field :organized_events, :paginated_event_list,
|
||||||
|
meta: [private: true, rule: :"read:group:events"] do
|
||||||
arg(:after_datetime, :datetime,
|
arg(:after_datetime, :datetime,
|
||||||
default_value: nil,
|
default_value: nil,
|
||||||
description: "Filter events that begin after this datetime"
|
description: "Filter events that begin after this datetime"
|
||||||
|
@ -94,7 +98,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
description("A list of the events this actor has organized")
|
description("A list of the events this actor has organized")
|
||||||
end
|
end
|
||||||
|
|
||||||
field :discussions, :paginated_discussion_list do
|
field :discussions, :paginated_discussion_list,
|
||||||
|
meta: [private: true, rule: :"read:group:discussions"] do
|
||||||
arg(:page, :integer,
|
arg(:page, :integer,
|
||||||
default_value: 1,
|
default_value: 1,
|
||||||
description: "The page in the paginated discussion list"
|
description: "The page in the paginated discussion list"
|
||||||
|
@ -111,7 +116,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
description: "Whether the group is opened to all or has restricted access"
|
description: "Whether the group is opened to all or has restricted access"
|
||||||
)
|
)
|
||||||
|
|
||||||
field :members, :paginated_member_list do
|
field :members, :paginated_member_list, meta: [private: true, rule: :"read:group:members"] do
|
||||||
arg(:name, :string, description: "A name to filter members by")
|
arg(:name, :string, description: "A name to filter members by")
|
||||||
arg(:page, :integer, default_value: 1, description: "The page in the paginated member list")
|
arg(:page, :integer, default_value: 1, description: "The page in the paginated member list")
|
||||||
arg(:limit, :integer, default_value: 10, description: "The limit of members per page")
|
arg(:limit, :integer, default_value: 10, description: "The limit of members per page")
|
||||||
|
@ -120,7 +125,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
description("A paginated list of group members")
|
description("A paginated list of group members")
|
||||||
end
|
end
|
||||||
|
|
||||||
field :resources, :paginated_resource_list do
|
field :resources, :paginated_resource_list,
|
||||||
|
meta: [private: true, rule: :"read:group:resources"] do
|
||||||
arg(:page, :integer,
|
arg(:page, :integer,
|
||||||
default_value: 1,
|
default_value: 1,
|
||||||
description: "The page in the paginated resource list"
|
description: "The page in the paginated resource list"
|
||||||
|
@ -138,7 +144,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
description("A paginated list of the posts this group has")
|
description("A paginated list of the posts this group has")
|
||||||
end
|
end
|
||||||
|
|
||||||
field :todo_lists, :paginated_todo_list_list do
|
field :todo_lists, :paginated_todo_list_list,
|
||||||
|
meta: [private: true, rule: :"read:group:todo_lists"] do
|
||||||
arg(:page, :integer,
|
arg(:page, :integer,
|
||||||
default_value: 1,
|
default_value: 1,
|
||||||
description: "The page in the paginated todo-lists list"
|
description: "The page in the paginated todo-lists list"
|
||||||
|
@ -149,7 +156,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
description("A paginated list of the todo lists this group has")
|
description("A paginated list of the todo lists this group has")
|
||||||
end
|
end
|
||||||
|
|
||||||
field :followers, :paginated_follower_list do
|
field :followers, :paginated_follower_list,
|
||||||
|
meta: [private: true, rule: :"read:group:followers"] do
|
||||||
arg(:page, :integer,
|
arg(:page, :integer,
|
||||||
default_value: 1,
|
default_value: 1,
|
||||||
description: "The page in the paginated followers list"
|
description: "The page in the paginated followers list"
|
||||||
|
@ -166,7 +174,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
description("A paginated list of the followers this group has")
|
description("A paginated list of the followers this group has")
|
||||||
end
|
end
|
||||||
|
|
||||||
field :activity, :paginated_activity_list do
|
field :activity, :paginated_activity_list,
|
||||||
|
meta: [private: true, rule: :"read:group:activities"] do
|
||||||
arg(:page, :integer,
|
arg(:page, :integer,
|
||||||
default_value: 1,
|
default_value: 1,
|
||||||
description: "The page in the paginated activity items list"
|
description: "The page in the paginated activity items list"
|
||||||
|
@ -204,6 +213,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
A paginated list of groups
|
A paginated list of groups
|
||||||
"""
|
"""
|
||||||
object :paginated_group_list do
|
object :paginated_group_list do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:elements, list_of(:group), description: "A list of groups")
|
field(:elements, list_of(:group), description: "A list of groups")
|
||||||
field(:total, :integer, description: "The total number of groups in the list")
|
field(:total, :integer, description: "The total number of groups in the list")
|
||||||
end
|
end
|
||||||
|
@ -215,12 +225,6 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
value(:private, description: "Visible only to people with the link - or invited")
|
value(:private, description: "Visible only to people with the link - or invited")
|
||||||
end
|
end
|
||||||
|
|
||||||
object :group_follow do
|
|
||||||
field(:group, :group, description: "The group followed")
|
|
||||||
field(:profile, :group, description: "The group followed")
|
|
||||||
field(:notify, :boolean, description: "Whether to notify profile from group activity")
|
|
||||||
end
|
|
||||||
|
|
||||||
object :group_queries do
|
object :group_queries do
|
||||||
@desc "Get all groups"
|
@desc "Get all groups"
|
||||||
field :groups, :paginated_group_list do
|
field :groups, :paginated_group_list do
|
||||||
|
@ -236,12 +240,25 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
arg(:suspended, :boolean, default_value: false, description: "Filter by suspended status")
|
arg(:suspended, :boolean, default_value: false, description: "Filter by suspended status")
|
||||||
arg(:page, :integer, default_value: 1, description: "The page in the paginated group list")
|
arg(:page, :integer, default_value: 1, description: "The page in the paginated group list")
|
||||||
arg(:limit, :integer, default_value: 10, description: "The limit of groups per page")
|
arg(:limit, :integer, default_value: 10, description: "The limit of groups per page")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: [:administrator, :moderator],
|
||||||
|
scope: Mobilizon.Actors.Actor,
|
||||||
|
args: %{}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Group.list_groups/3)
|
resolve(&Group.list_groups/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Get a group by its ID"
|
@desc "Get a group by its ID"
|
||||||
field :get_group, :group do
|
field :get_group, :group do
|
||||||
arg(:id, non_null(:id), description: "The group ID")
|
arg(:id, non_null(:id), description: "The group ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: [:administrator, :moderator],
|
||||||
|
scope: Mobilizon.Actors.Actor
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Group.get_group/3)
|
resolve(&Group.get_group/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -251,15 +268,9 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
description: "The group preferred_username, eventually containing their domain if remote"
|
description: "The group preferred_username, eventually containing their domain if remote"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&Group.find_group/3)
|
resolve(&Group.find_group/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Get a group by its preferred username"
|
|
||||||
field :group_by_id, :group do
|
|
||||||
arg(:id, non_null(:id), description: "The group local ID")
|
|
||||||
|
|
||||||
resolve(&Group.find_group_by_id/3)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
object :group_mutations do
|
object :group_mutations do
|
||||||
|
@ -291,7 +302,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
)
|
)
|
||||||
|
|
||||||
arg(:physical_address, :address_input, description: "The physical address for the group")
|
arg(:physical_address, :address_input, description: "The physical address for the group")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&Group.create_group/3)
|
resolve(&Group.create_group/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -323,14 +334,14 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
)
|
)
|
||||||
|
|
||||||
arg(:physical_address, :address_input, description: "The physical address for the group")
|
arg(:physical_address, :address_input, description: "The physical address for the group")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&Group.update_group/3)
|
resolve(&Group.update_group/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Delete a group"
|
@desc "Delete a group"
|
||||||
field :delete_group, :deleted_object do
|
field :delete_group, :deleted_object do
|
||||||
arg(:group_id, non_null(:id), description: "The group ID")
|
arg(:group_id, non_null(:id), description: "The group ID")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&Group.delete_group/3)
|
resolve(&Group.delete_group/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -343,6 +354,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
default_value: true
|
default_value: true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&Group.follow_group/3)
|
resolve(&Group.follow_group/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -355,13 +367,14 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||||
default_value: true
|
default_value: true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&Group.update_group_follow/3)
|
resolve(&Group.update_group_follow/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Unfollow a group"
|
@desc "Unfollow a group"
|
||||||
field :unfollow_group, :follower do
|
field :unfollow_group, :follower do
|
||||||
arg(:group_id, non_null(:id), description: "The group ID")
|
arg(:group_id, non_null(:id), description: "The group ID")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&Group.unfollow_group/3)
|
resolve(&Group.unfollow_group/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,6 +10,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
|
||||||
Represents a member of a group
|
Represents a member of a group
|
||||||
"""
|
"""
|
||||||
object :member do
|
object :member do
|
||||||
|
meta(:authorize, :user)
|
||||||
interfaces([:activity_object])
|
interfaces([:activity_object])
|
||||||
field(:id, :id, description: "The member's ID")
|
field(:id, :id, description: "The member's ID")
|
||||||
field(:parent, :group, description: "Of which the profile is member")
|
field(:parent, :group, description: "Of which the profile is member")
|
||||||
|
@ -37,6 +38,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
|
||||||
A paginated list of members
|
A paginated list of members
|
||||||
"""
|
"""
|
||||||
object :paginated_member_list do
|
object :paginated_member_list do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:elements, list_of(:member), description: "A list of members")
|
field(:elements, list_of(:member), description: "A list of members")
|
||||||
field(:total, :integer, description: "The total number of elements in the list")
|
field(:total, :integer, description: "The total number of elements in the list")
|
||||||
end
|
end
|
||||||
|
@ -46,6 +48,13 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
|
||||||
field :join_group, :member do
|
field :join_group, :member do
|
||||||
arg(:group_id, non_null(:id), description: "The group ID")
|
arg(:group_id, non_null(:id), description: "The group ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Member,
|
||||||
|
rule: :"write:group_membership",
|
||||||
|
args: %{parent_id: :group_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Group.join_group/3)
|
resolve(&Group.join_group/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -53,9 +62,42 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
|
||||||
field :leave_group, :deleted_object do
|
field :leave_group, :deleted_object do
|
||||||
arg(:group_id, non_null(:id), description: "The group ID")
|
arg(:group_id, non_null(:id), description: "The group ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Member,
|
||||||
|
rule: :"write:group_membership",
|
||||||
|
args: %{parent_id: :group_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Group.leave_group/3)
|
resolve(&Group.leave_group/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@desc "Accept an invitation to a group"
|
||||||
|
field :accept_invitation, :member do
|
||||||
|
arg(:id, non_null(:id), description: "The member ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Member,
|
||||||
|
rule: :"write:group_membership"
|
||||||
|
)
|
||||||
|
|
||||||
|
resolve(&Member.accept_invitation/3)
|
||||||
|
end
|
||||||
|
|
||||||
|
@desc "Reject an invitation to a group"
|
||||||
|
field :reject_invitation, :member do
|
||||||
|
arg(:id, non_null(:id), description: "The member ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Member,
|
||||||
|
rule: :"write:group_membership"
|
||||||
|
)
|
||||||
|
|
||||||
|
resolve(&Member.reject_invitation/3)
|
||||||
|
end
|
||||||
|
|
||||||
@desc "Invite an actor to join the group"
|
@desc "Invite an actor to join the group"
|
||||||
field :invite_member, :member do
|
field :invite_member, :member do
|
||||||
arg(:group_id, non_null(:id), description: "The group ID")
|
arg(:group_id, non_null(:id), description: "The group ID")
|
||||||
|
@ -64,29 +106,29 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
|
||||||
description: "The targeted person's federated username"
|
description: "The targeted person's federated username"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Member,
|
||||||
|
rule: :"write:group:members",
|
||||||
|
args: %{parent_id: :group_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Member.invite_member/3)
|
resolve(&Member.invite_member/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Accept an invitation to a group"
|
|
||||||
field :accept_invitation, :member do
|
|
||||||
arg(:id, non_null(:id), description: "The member ID")
|
|
||||||
|
|
||||||
resolve(&Member.accept_invitation/3)
|
|
||||||
end
|
|
||||||
|
|
||||||
@desc "Reject an invitation to a group"
|
|
||||||
field :reject_invitation, :member do
|
|
||||||
arg(:id, non_null(:id), description: "The member ID")
|
|
||||||
|
|
||||||
resolve(&Member.reject_invitation/3)
|
|
||||||
end
|
|
||||||
|
|
||||||
@desc """
|
@desc """
|
||||||
Approve a membership request
|
Approve a membership request
|
||||||
"""
|
"""
|
||||||
field :approve_member, :member do
|
field :approve_member, :member do
|
||||||
arg(:member_id, non_null(:id), description: "The member ID")
|
arg(:member_id, non_null(:id), description: "The member ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Member,
|
||||||
|
rule: :"write:group:members",
|
||||||
|
args: %{parent_id: :member_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Member.approve_member/3)
|
resolve(&Member.approve_member/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -96,6 +138,13 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
|
||||||
field :reject_member, :member do
|
field :reject_member, :member do
|
||||||
arg(:member_id, non_null(:id), description: "The member ID")
|
arg(:member_id, non_null(:id), description: "The member ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Member,
|
||||||
|
rule: :"write:group:members",
|
||||||
|
args: %{parent_id: :member_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Member.reject_member/3)
|
resolve(&Member.reject_member/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -106,6 +155,13 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
|
||||||
arg(:member_id, non_null(:id), description: "The member ID")
|
arg(:member_id, non_null(:id), description: "The member ID")
|
||||||
arg(:role, non_null(:member_role_enum), description: "The new member role")
|
arg(:role, non_null(:member_role_enum), description: "The new member role")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Member,
|
||||||
|
rule: :"write:group:members",
|
||||||
|
args: %{parent_id: :member_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Member.update_member/3)
|
resolve(&Member.update_member/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -118,6 +174,13 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
|
||||||
description: "Whether the member should be excluded from the group"
|
description: "Whether the member should be excluded from the group"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Member,
|
||||||
|
rule: :"write:group:members",
|
||||||
|
args: %{parent_id: :member_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Member.remove_member/3)
|
resolve(&Member.remove_member/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
Represents a person identity
|
Represents a person identity
|
||||||
"""
|
"""
|
||||||
object :person do
|
object :person do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
meta(:scope_field?, true)
|
||||||
interfaces([:actor, :action_log_object])
|
interfaces([:actor, :action_log_object])
|
||||||
field(:id, :id, description: "Internal ID for this person")
|
field(:id, :id, description: "Internal ID for this person")
|
||||||
|
|
||||||
|
@ -72,7 +74,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
|
|
||||||
# This one should have a privacy setting
|
# This one should have a privacy setting
|
||||||
field(:organized_events, :paginated_event_list,
|
field(:organized_events, :paginated_event_list,
|
||||||
description: "A list of the events this actor has organized"
|
description: "A list of the events this actor has organized",
|
||||||
|
meta: [private: true, rule: :"read:profile:organized_events"]
|
||||||
) do
|
) do
|
||||||
arg(:page, :integer, default_value: 1, description: "The page in the paginated event list")
|
arg(:page, :integer, default_value: 1, description: "The page in the paginated event list")
|
||||||
arg(:limit, :integer, default_value: 10, description: "The limit of events per page")
|
arg(:limit, :integer, default_value: 10, description: "The limit of events per page")
|
||||||
|
@ -81,7 +84,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
|
|
||||||
@desc "The list of events this person goes to"
|
@desc "The list of events this person goes to"
|
||||||
field(:participations, :paginated_participant_list,
|
field(:participations, :paginated_participant_list,
|
||||||
description: "The list of events this person goes to"
|
description: "The list of events this person goes to",
|
||||||
|
meta: [private: true, rule: :"read:profile:participations"]
|
||||||
) do
|
) do
|
||||||
arg(:event_id, :id, description: "Filter by event ID")
|
arg(:event_id, :id, description: "Filter by event ID")
|
||||||
|
|
||||||
|
@ -97,7 +101,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
|
|
||||||
@desc "The list of groups this person is member of"
|
@desc "The list of groups this person is member of"
|
||||||
field(:memberships, :paginated_member_list,
|
field(:memberships, :paginated_member_list,
|
||||||
description: "The list of group this person is member of"
|
description: "The list of group this person is member of",
|
||||||
|
meta: [private: true, rule: :"read:profile:memberships"]
|
||||||
) do
|
) do
|
||||||
arg(:group, :string, description: "Filter by group federated username")
|
arg(:group, :string, description: "Filter by group federated username")
|
||||||
arg(:group_id, :id, description: "Filter by group ID")
|
arg(:group_id, :id, description: "Filter by group ID")
|
||||||
|
@ -113,7 +118,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
|
|
||||||
@desc "The list of groups this person follows"
|
@desc "The list of groups this person follows"
|
||||||
field(:follows, :paginated_follower_list,
|
field(:follows, :paginated_follower_list,
|
||||||
description: "The list of groups this person follows"
|
description: "The list of groups this person follows",
|
||||||
|
meta: [private: true, rule: :"read:profile:follows"]
|
||||||
) do
|
) do
|
||||||
arg(:group, :string, description: "Filter by group federated username")
|
arg(:group, :string, description: "Filter by group federated username")
|
||||||
|
|
||||||
|
@ -131,6 +137,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
A paginated list of persons
|
A paginated list of persons
|
||||||
"""
|
"""
|
||||||
object :paginated_person_list do
|
object :paginated_person_list do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:elements, list_of(:person), description: "A list of persons")
|
field(:elements, list_of(:person), description: "A list of persons")
|
||||||
field(:total, :integer, description: "The total number of persons in the list")
|
field(:total, :integer, description: "The total number of persons in the list")
|
||||||
end
|
end
|
||||||
|
@ -138,23 +145,46 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
object :person_queries do
|
object :person_queries do
|
||||||
@desc "Get the current actor for the logged-in user"
|
@desc "Get the current actor for the logged-in user"
|
||||||
field :logged_person, :person do
|
field :logged_person, :person do
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Actor,
|
||||||
|
args: %{}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Person.get_current_person/3)
|
resolve(&Person.get_current_person/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Get a person by its (federated) username"
|
@desc "Get a person by its (federated) username"
|
||||||
field :fetch_person, :person do
|
field :fetch_person, :person do
|
||||||
arg(:preferred_username, non_null(:string), description: "The person's federated username")
|
arg(:preferred_username, non_null(:string), description: "The person's federated username")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Actor,
|
||||||
|
args: %{preferred_username: :preferred_username}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Person.fetch_person/3)
|
resolve(&Person.fetch_person/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Get a person by its ID"
|
@desc "Get a person by its ID"
|
||||||
field :person, :person do
|
field :person, :person do
|
||||||
arg(:id, non_null(:id), description: "The person ID")
|
arg(:id, non_null(:id), description: "The person ID")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&Person.get_person/3)
|
resolve(&Person.get_person/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Get the persons for an user"
|
@desc "Get the persons for an user"
|
||||||
field :identities, list_of(:person) do
|
field :identities, list_of(:person) do
|
||||||
|
deprecate("Use the loggedUser query instead")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: [:user, :moderator, :administrator],
|
||||||
|
scope: Mobilizon.Actors.Actor,
|
||||||
|
args: %{},
|
||||||
|
rule: :user_self_identities
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Person.identities/3)
|
resolve(&Person.identities/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -172,6 +202,13 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
arg(:suspended, :boolean, default_value: false, description: "Filter by suspended status")
|
arg(:suspended, :boolean, default_value: false, description: "Filter by suspended status")
|
||||||
arg(:page, :integer, default_value: 1, description: "The page in the paginated person list")
|
arg(:page, :integer, default_value: 1, description: "The page in the paginated person list")
|
||||||
arg(:limit, :integer, default_value: 10, description: "The limit of persons per page")
|
arg(:limit, :integer, default_value: 10, description: "The limit of persons per page")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: [:administrator, :moderator],
|
||||||
|
scope: Mobilizon.Actors.Actor,
|
||||||
|
args: %{}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Person.list_persons/3)
|
resolve(&Person.list_persons/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -195,6 +232,13 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
"The banner for the profile, either as an object or directly the ID of an existing media"
|
"The banner for the profile, either as an object or directly the ID of an existing media"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Actor,
|
||||||
|
args: %{},
|
||||||
|
rule: :"write:profile:create"
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Person.create_person/3)
|
resolve(&Person.create_person/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -216,6 +260,12 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
"The banner for the profile, either as an object or directly the ID of an existing media"
|
"The banner for the profile, either as an object or directly the ID of an existing media"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Actor,
|
||||||
|
rule: :"write:profile:update"
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Person.update_person/3)
|
resolve(&Person.update_person/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -223,6 +273,12 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
field :delete_person, :person do
|
field :delete_person, :person do
|
||||||
arg(:id, non_null(:id), description: "The person's ID")
|
arg(:id, non_null(:id), description: "The person's ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Actor,
|
||||||
|
rule: :"write:profile:delete"
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Person.delete_person/3)
|
resolve(&Person.delete_person/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -245,6 +301,8 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
"The banner for the profile, either as an object or directly the ID of an existing media"
|
"The banner for the profile, either as an object or directly the ID of an existing media"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all, scope: Mobilizon.Actors.Actor, args: %{})
|
||||||
|
|
||||||
resolve(&Person.register_person/3)
|
resolve(&Person.register_person/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -254,6 +312,12 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
field :event_person_participation_changed, :person do
|
field :event_person_participation_changed, :person do
|
||||||
arg(:person_id, non_null(:id), description: "The person's ID")
|
arg(:person_id, non_null(:id), description: "The person's ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Actor,
|
||||||
|
args: %{id: :person_id}
|
||||||
|
)
|
||||||
|
|
||||||
config(fn args, _ ->
|
config(fn args, _ ->
|
||||||
{:ok, topic: args.person_id}
|
{:ok, topic: args.person_id}
|
||||||
end)
|
end)
|
||||||
|
@ -264,6 +328,12 @@ defmodule Mobilizon.GraphQL.Schema.Actors.PersonType do
|
||||||
arg(:person_id, non_null(:id), description: "The person's ID")
|
arg(:person_id, non_null(:id), description: "The person's ID")
|
||||||
arg(:group, non_null(:string), description: "The group's federated username")
|
arg(:group, non_null(:string), description: "The group's federated username")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Actors.Actor,
|
||||||
|
args: %{id: :person_id}
|
||||||
|
)
|
||||||
|
|
||||||
config(fn args, _ ->
|
config(fn args, _ ->
|
||||||
{:ok, topic: [args.group, args.person_id]}
|
{:ok, topic: [args.group, args.person_id]}
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -10,6 +10,7 @@ defmodule Mobilizon.GraphQL.Schema.AddressType do
|
||||||
An address object
|
An address object
|
||||||
"""
|
"""
|
||||||
object :address do
|
object :address do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:geom, :point, description: "The geocoordinates for the point where this address is")
|
field(:geom, :point, description: "The geocoordinates for the point where this address is")
|
||||||
field(:street, :string, description: "The address's street name (with number)")
|
field(:street, :string, description: "The address's street name (with number)")
|
||||||
field(:locality, :string, description: "The address's locality")
|
field(:locality, :string, description: "The address's locality")
|
||||||
|
@ -29,6 +30,7 @@ defmodule Mobilizon.GraphQL.Schema.AddressType do
|
||||||
A phone address
|
A phone address
|
||||||
"""
|
"""
|
||||||
object :phone_address do
|
object :phone_address do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:phone, :string, description: "The phone number")
|
field(:phone, :string, description: "The phone number")
|
||||||
field(:info, :string, description: "Additional information about the phone number")
|
field(:info, :string, description: "Additional information about the phone number")
|
||||||
end
|
end
|
||||||
|
@ -37,11 +39,13 @@ defmodule Mobilizon.GraphQL.Schema.AddressType do
|
||||||
An online address
|
An online address
|
||||||
"""
|
"""
|
||||||
object :online_address do
|
object :online_address do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:url, :string)
|
field(:url, :string)
|
||||||
field(:info, :string)
|
field(:info, :string)
|
||||||
end
|
end
|
||||||
|
|
||||||
object :picture_info_element do
|
object :picture_info_element do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:name, :string)
|
field(:name, :string)
|
||||||
field(:url, :string)
|
field(:url, :string)
|
||||||
end
|
end
|
||||||
|
@ -50,6 +54,7 @@ defmodule Mobilizon.GraphQL.Schema.AddressType do
|
||||||
A picture associated with an address
|
A picture associated with an address
|
||||||
"""
|
"""
|
||||||
object :picture_info do
|
object :picture_info do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:url, :string)
|
field(:url, :string)
|
||||||
field(:author, :picture_info_element)
|
field(:author, :picture_info_element)
|
||||||
field(:source, :picture_info_element)
|
field(:source, :picture_info_element)
|
||||||
|
@ -100,7 +105,7 @@ defmodule Mobilizon.GraphQL.Schema.AddressType do
|
||||||
arg(:limit, :integer, default_value: 10, description: "The limit of search results per page")
|
arg(:limit, :integer, default_value: 10, description: "The limit of search results per page")
|
||||||
|
|
||||||
arg(:type, :address_search_type, description: "Filter by type of results")
|
arg(:type, :address_search_type, description: "Filter by type of results")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&Address.search/3)
|
resolve(&Address.search/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -115,6 +120,7 @@ defmodule Mobilizon.GraphQL.Schema.AddressType do
|
||||||
description: "The user's locale. Geocoding backends will make use of this value."
|
description: "The user's locale. Geocoding backends will make use of this value."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&Address.reverse_geocode/3)
|
resolve(&Address.reverse_geocode/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,6 +15,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
|
|
||||||
@desc "An action log"
|
@desc "An action log"
|
||||||
object :action_log do
|
object :action_log do
|
||||||
|
meta(:authorize, :moderator)
|
||||||
field(:id, :id, description: "Internal ID for this comment")
|
field(:id, :id, description: "Internal ID for this comment")
|
||||||
field(:actor, :actor, description: "The actor that acted")
|
field(:actor, :actor, description: "The actor that acted")
|
||||||
field(:object, :action_log_object, description: "The object that was acted upon")
|
field(:object, :action_log_object, description: "The object that was acted upon")
|
||||||
|
@ -26,6 +27,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
A paginated list of action logs
|
A paginated list of action logs
|
||||||
"""
|
"""
|
||||||
object :paginated_action_log_list do
|
object :paginated_action_log_list do
|
||||||
|
meta(:authorize, :moderator)
|
||||||
field(:elements, list_of(:action_log), description: "A list of action logs")
|
field(:elements, list_of(:action_log), description: "A list of action logs")
|
||||||
field(:total, :integer, description: "The total number of action logs in the list")
|
field(:total, :integer, description: "The total number of action logs in the list")
|
||||||
end
|
end
|
||||||
|
@ -49,6 +51,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
|
|
||||||
@desc "The objects that can be in an action log"
|
@desc "The objects that can be in an action log"
|
||||||
interface :action_log_object do
|
interface :action_log_object do
|
||||||
|
meta(:authorize, [:moderator, :administrator])
|
||||||
field(:id, :id, description: "Internal ID for this object")
|
field(:id, :id, description: "Internal ID for this object")
|
||||||
|
|
||||||
resolve_type(fn
|
resolve_type(fn
|
||||||
|
@ -82,6 +85,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
Language information
|
Language information
|
||||||
"""
|
"""
|
||||||
object :language do
|
object :language do
|
||||||
|
meta(:authorize, :administrator)
|
||||||
field(:code, :string, description: "The iso-639-3 language code")
|
field(:code, :string, description: "The iso-639-3 language code")
|
||||||
field(:name, :string, description: "The language name")
|
field(:name, :string, description: "The language name")
|
||||||
end
|
end
|
||||||
|
@ -90,6 +94,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
Dashboard information
|
Dashboard information
|
||||||
"""
|
"""
|
||||||
object :dashboard do
|
object :dashboard do
|
||||||
|
meta(:authorize, :administrator)
|
||||||
field(:last_public_event_published, :event, description: "Last public event published")
|
field(:last_public_event_published, :event, description: "Last public event published")
|
||||||
field(:last_group_created, :group, description: "Last public group created")
|
field(:last_group_created, :group, description: "Last public group created")
|
||||||
field(:number_of_users, :integer, description: "The number of local users")
|
field(:number_of_users, :integer, description: "The number of local users")
|
||||||
|
@ -109,6 +114,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
Admin settings
|
Admin settings
|
||||||
"""
|
"""
|
||||||
object :admin_settings do
|
object :admin_settings do
|
||||||
|
meta(:authorize, :administrator)
|
||||||
field(:instance_name, :string, description: "The instance's name")
|
field(:instance_name, :string, description: "The instance's name")
|
||||||
field(:instance_description, :string, description: "The instance's description")
|
field(:instance_description, :string, description: "The instance's description")
|
||||||
field(:instance_long_description, :string, description: "The instance's long description")
|
field(:instance_long_description, :string, description: "The instance's long description")
|
||||||
|
@ -184,6 +190,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
An instance representation
|
An instance representation
|
||||||
"""
|
"""
|
||||||
object :instance do
|
object :instance do
|
||||||
|
meta(:authorize, :administrator)
|
||||||
field(:domain, :id, description: "The domain name of the instance")
|
field(:domain, :id, description: "The domain name of the instance")
|
||||||
field(:follower_status, :instance_follow_status, description: "Do we follow this instance")
|
field(:follower_status, :instance_follow_status, description: "Do we follow this instance")
|
||||||
field(:followed_status, :instance_follow_status, description: "Does this instance follow us?")
|
field(:followed_status, :instance_follow_status, description: "Does this instance follow us?")
|
||||||
|
@ -226,6 +233,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
A paginated list of instances
|
A paginated list of instances
|
||||||
"""
|
"""
|
||||||
object :paginated_instance_list do
|
object :paginated_instance_list do
|
||||||
|
meta(:authorize, :administrator)
|
||||||
field(:elements, list_of(:instance), description: "A list of instances")
|
field(:elements, list_of(:instance), description: "A list of instances")
|
||||||
field(:total, :integer, description: "The total number of instances in the list")
|
field(:total, :integer, description: "The total number of instances in the list")
|
||||||
end
|
end
|
||||||
|
@ -235,6 +243,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
field :action_logs, type: :paginated_action_log_list do
|
field :action_logs, type: :paginated_action_log_list do
|
||||||
arg(:page, :integer, default_value: 1)
|
arg(:page, :integer, default_value: 1)
|
||||||
arg(:limit, :integer, default_value: 10)
|
arg(:limit, :integer, default_value: 10)
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :moderator, scope: false)
|
||||||
resolve(&Admin.list_action_logs/3)
|
resolve(&Admin.list_action_logs/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -247,6 +256,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
"The user's locale. The list of languages will be translated with this locale"
|
"The user's locale. The list of languages will be translated with this locale"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&Admin.get_list_of_languages/3)
|
resolve(&Admin.get_list_of_languages/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -254,6 +264,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
Get dashboard information
|
Get dashboard information
|
||||||
"""
|
"""
|
||||||
field :dashboard, type: :dashboard do
|
field :dashboard, type: :dashboard do
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :administrator)
|
||||||
resolve(&Admin.get_dashboard/3)
|
resolve(&Admin.get_dashboard/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -261,6 +272,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
Get admin settings
|
Get admin settings
|
||||||
"""
|
"""
|
||||||
field :admin_settings, type: :admin_settings do
|
field :admin_settings, type: :admin_settings do
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :administrator)
|
||||||
resolve(&Admin.get_settings/3)
|
resolve(&Admin.get_settings/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -278,6 +290,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
description: "The limit of relay followers per page"
|
description: "The limit of relay followers per page"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :administrator)
|
||||||
resolve(&Admin.list_relay_followers/3)
|
resolve(&Admin.list_relay_followers/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -301,6 +314,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
)
|
)
|
||||||
|
|
||||||
arg(:direction, :string, default_value: :desc, description: "The sorting direction")
|
arg(:direction, :string, default_value: :desc, description: "The sorting direction")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :administrator)
|
||||||
resolve(&Admin.list_relay_followings/3)
|
resolve(&Admin.list_relay_followings/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -336,6 +350,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
)
|
)
|
||||||
|
|
||||||
arg(:direction, :string, default_value: :desc, description: "The sorting direction")
|
arg(:direction, :string, default_value: :desc, description: "The sorting direction")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :administrator)
|
||||||
resolve(&Admin.get_instances/3)
|
resolve(&Admin.get_instances/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -344,6 +359,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
"""
|
"""
|
||||||
field :instance, :instance do
|
field :instance, :instance do
|
||||||
arg(:domain, non_null(:id), description: "The instance domain")
|
arg(:domain, non_null(:id), description: "The instance domain")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :administrator)
|
||||||
resolve(&Admin.get_instance/3)
|
resolve(&Admin.get_instance/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -352,28 +368,28 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
@desc "Add an instance subscription"
|
@desc "Add an instance subscription"
|
||||||
field :add_instance, type: :instance do
|
field :add_instance, type: :instance do
|
||||||
arg(:domain, non_null(:string), description: "The instance domain to add")
|
arg(:domain, non_null(:string), description: "The instance domain to add")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :administrator)
|
||||||
resolve(&Admin.create_instance/3)
|
resolve(&Admin.create_instance/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Delete a relay subscription"
|
@desc "Delete a relay subscription"
|
||||||
field :remove_relay, type: :follower do
|
field :remove_relay, type: :follower do
|
||||||
arg(:address, non_null(:string), description: "The relay hostname to delete")
|
arg(:address, non_null(:string), description: "The relay hostname to delete")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :administrator)
|
||||||
resolve(&Admin.remove_relay/3)
|
resolve(&Admin.remove_relay/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Accept a relay subscription"
|
@desc "Accept a relay subscription"
|
||||||
field :accept_relay, type: :follower do
|
field :accept_relay, type: :follower do
|
||||||
arg(:address, non_null(:string), description: "The accepted relay hostname")
|
arg(:address, non_null(:string), description: "The accepted relay hostname")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :administrator)
|
||||||
resolve(&Admin.accept_subscription/3)
|
resolve(&Admin.accept_subscription/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Reject a relay subscription"
|
@desc "Reject a relay subscription"
|
||||||
field :reject_relay, type: :follower do
|
field :reject_relay, type: :follower do
|
||||||
arg(:address, non_null(:string), description: "The rejected relay hostname")
|
arg(:address, non_null(:string), description: "The rejected relay hostname")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :administrator)
|
||||||
resolve(&Admin.reject_subscription/3)
|
resolve(&Admin.reject_subscription/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -402,7 +418,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
arg(:instance_rules, :string, description: "The instance's rules")
|
arg(:instance_rules, :string, description: "The instance's rules")
|
||||||
arg(:registrations_open, :boolean, description: "Whether the registrations are opened")
|
arg(:registrations_open, :boolean, description: "Whether the registrations are opened")
|
||||||
arg(:instance_languages, list_of(:string), description: "The instance's languages")
|
arg(:instance_languages, list_of(:string), description: "The instance's languages")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :administrator)
|
||||||
resolve(&Admin.save_settings/3)
|
resolve(&Admin.save_settings/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -420,6 +436,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do
|
||||||
description: "Whether or not to notify the user of the change"
|
description: "Whether or not to notify the user of the change"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :administrator)
|
||||||
resolve(&Admin.update_user/3)
|
resolve(&Admin.update_user/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,15 +7,17 @@ defmodule Mobilizon.GraphQL.Schema.AuthApplicationType do
|
||||||
|
|
||||||
@desc "An application"
|
@desc "An application"
|
||||||
object :auth_application do
|
object :auth_application do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:id, :id)
|
field(:id, :id)
|
||||||
field(:name, :string)
|
field(:name, :string)
|
||||||
field(:client_id, :string)
|
field(:client_id, :string)
|
||||||
field(:scopes, :string)
|
field(:scope, :string)
|
||||||
field(:website, :string)
|
field(:website, :string)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "An application"
|
@desc "An application"
|
||||||
object :auth_application_token do
|
object :auth_application_token do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:id, :id)
|
field(:id, :id)
|
||||||
field(:inserted_at, :string)
|
field(:inserted_at, :string)
|
||||||
field(:last_used_at, :string)
|
field(:last_used_at, :string)
|
||||||
|
@ -24,11 +26,15 @@ defmodule Mobilizon.GraphQL.Schema.AuthApplicationType do
|
||||||
|
|
||||||
@desc "The informations returned after authorization"
|
@desc "The informations returned after authorization"
|
||||||
object :application_code_and_state do
|
object :application_code_and_state do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:code, :string)
|
field(:code, :string)
|
||||||
field(:state, :string)
|
field(:state, :string)
|
||||||
|
field(:client_id, :string)
|
||||||
|
field(:scope, :string)
|
||||||
end
|
end
|
||||||
|
|
||||||
object :application_device_activation do
|
object :application_device_activation do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:id, :id)
|
field(:id, :id)
|
||||||
field(:application, :auth_application)
|
field(:application, :auth_application)
|
||||||
field(:scope, :string)
|
field(:scope, :string)
|
||||||
|
@ -38,6 +44,14 @@ defmodule Mobilizon.GraphQL.Schema.AuthApplicationType do
|
||||||
@desc "Get an application"
|
@desc "Get an application"
|
||||||
field :auth_application, :auth_application do
|
field :auth_application, :auth_application do
|
||||||
arg(:client_id, non_null(:string), description: "The application's client_id")
|
arg(:client_id, non_null(:string), description: "The application's client_id")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Applications.Application,
|
||||||
|
rule: :forbid_app_access,
|
||||||
|
args: %{client_id: :client_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Application.get_application/3)
|
resolve(&Application.get_application/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -51,18 +65,33 @@ defmodule Mobilizon.GraphQL.Schema.AuthApplicationType do
|
||||||
description: "The URI to redirect to with the code and state"
|
description: "The URI to redirect to with the code and state"
|
||||||
)
|
)
|
||||||
|
|
||||||
arg(:scope, :string, description: "The scope for the authorization")
|
arg(:scope, non_null(:string), description: "The scope for the authorization")
|
||||||
|
|
||||||
arg(:state, :string,
|
arg(:state, :string,
|
||||||
description: "A state parameter to check that the request wasn't altered"
|
description: "A state parameter to check that the request wasn't altered"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Applications.Application,
|
||||||
|
rule: :forbid_app_access,
|
||||||
|
args: %{client_id: :client_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Application.authorize/3)
|
resolve(&Application.authorize/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Revoke an authorized application"
|
@desc "Revoke an authorized application"
|
||||||
field :revoke_application_token, :deleted_object do
|
field :revoke_application_token, :deleted_object do
|
||||||
arg(:app_token_id, non_null(:string), description: "The application token's ID")
|
arg(:app_token_id, non_null(:string), description: "The application token's ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Applications.ApplicationToken,
|
||||||
|
rule: :forbid_app_access,
|
||||||
|
args: %{id: :app_token_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Application.revoke_application_token/3)
|
resolve(&Application.revoke_application_token/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -72,13 +101,30 @@ defmodule Mobilizon.GraphQL.Schema.AuthApplicationType do
|
||||||
description: "The code provided by the application entered by the user"
|
description: "The code provided by the application entered by the user"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Applications.ApplicationDeviceActivation,
|
||||||
|
rule: :forbid_app_access,
|
||||||
|
args: %{id: :user_code}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Application.activate_device/3)
|
resolve(&Application.activate_device/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Activate an user device"
|
@desc "Activate an user device"
|
||||||
field :authorize_device_application, :auth_application do
|
field :authorize_device_application, :auth_application do
|
||||||
arg(:client_id, non_null(:string), description: "The application's client_id")
|
arg(:client_id, non_null(:string), description: "The application's client_id")
|
||||||
arg(:scope, :string, description: "The scope for the authorization")
|
|
||||||
|
arg(:user_code, non_null(:string),
|
||||||
|
description: "The code provided by the application entered by the user"
|
||||||
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Applications.ApplicationDeviceActivation,
|
||||||
|
rule: :forbid_app_access,
|
||||||
|
args: %{id: :client_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Application.authorize_device_application/3)
|
resolve(&Application.authorize_device_application/3)
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
|
|
||||||
@desc "A config object"
|
@desc "A config object"
|
||||||
object :config do
|
object :config do
|
||||||
|
meta(:authorize, :all)
|
||||||
# Instance name
|
# Instance name
|
||||||
field(:name, :string, description: "The instance's name")
|
field(:name, :string, description: "The instance's name")
|
||||||
field(:description, :string, description: "The instance's short description")
|
field(:description, :string, description: "The instance's short description")
|
||||||
|
@ -87,6 +88,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
The instance's terms configuration
|
The instance's terms configuration
|
||||||
"""
|
"""
|
||||||
object :terms do
|
object :terms do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:url, :string, description: "The instance's terms URL.")
|
field(:url, :string, description: "The instance's terms URL.")
|
||||||
field(:type, :instance_terms_type, description: "The instance's terms type")
|
field(:type, :instance_terms_type, description: "The instance's terms type")
|
||||||
field(:body_html, :string, description: "The instance's terms body text")
|
field(:body_html, :string, description: "The instance's terms body text")
|
||||||
|
@ -96,6 +98,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
The instance's privacy policy configuration
|
The instance's privacy policy configuration
|
||||||
"""
|
"""
|
||||||
object :privacy do
|
object :privacy do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:url, :string, description: "The instance's privacy policy URL")
|
field(:url, :string, description: "The instance's privacy policy URL")
|
||||||
field(:type, :instance_privacy_type, description: "The instance's privacy policy type")
|
field(:type, :instance_privacy_type, description: "The instance's privacy policy type")
|
||||||
field(:body_html, :string, description: "The instance's privacy policy body text")
|
field(:body_html, :string, description: "The instance's privacy policy body text")
|
||||||
|
@ -105,6 +108,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Geographic coordinates
|
Geographic coordinates
|
||||||
"""
|
"""
|
||||||
object :lonlat do
|
object :lonlat do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:longitude, :float, description: "The coordinates longitude")
|
field(:longitude, :float, description: "The coordinates longitude")
|
||||||
field(:latitude, :float, description: "The coordinates latitude")
|
field(:latitude, :float, description: "The coordinates latitude")
|
||||||
# field(:accuracy_radius, :integer)
|
# field(:accuracy_radius, :integer)
|
||||||
|
@ -114,6 +118,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Instance geocoding configuration
|
Instance geocoding configuration
|
||||||
"""
|
"""
|
||||||
object :geocoding do
|
object :geocoding do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
|
||||||
field(:autocomplete, :boolean,
|
field(:autocomplete, :boolean,
|
||||||
description: "Whether autocomplete in address fields can be enabled"
|
description: "Whether autocomplete in address fields can be enabled"
|
||||||
)
|
)
|
||||||
|
@ -125,6 +131,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Instance maps configuration
|
Instance maps configuration
|
||||||
"""
|
"""
|
||||||
object :maps do
|
object :maps do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:tiles, :tiles, description: "The instance's maps tiles configuration")
|
field(:tiles, :tiles, description: "The instance's maps tiles configuration")
|
||||||
field(:routing, :routing, description: "The instance's maps routing configuration")
|
field(:routing, :routing, description: "The instance's maps routing configuration")
|
||||||
end
|
end
|
||||||
|
@ -133,6 +140,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Instance map tiles configuration
|
Instance map tiles configuration
|
||||||
"""
|
"""
|
||||||
object :tiles do
|
object :tiles do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:endpoint, :string, description: "The instance's tiles endpoint")
|
field(:endpoint, :string, description: "The instance's tiles endpoint")
|
||||||
field(:attribution, :string, description: "The instance's tiles attribution text")
|
field(:attribution, :string, description: "The instance's tiles attribution text")
|
||||||
end
|
end
|
||||||
|
@ -141,6 +149,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Instance map routing configuration
|
Instance map routing configuration
|
||||||
"""
|
"""
|
||||||
object :routing do
|
object :routing do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:type, :routing_type, description: "The instance's routing type")
|
field(:type, :routing_type, description: "The instance's routing type")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -153,6 +162,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Instance anonymous configuration
|
Instance anonymous configuration
|
||||||
"""
|
"""
|
||||||
object :anonymous do
|
object :anonymous do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
|
||||||
field(:participation, :anonymous_participation,
|
field(:participation, :anonymous_participation,
|
||||||
description: "The instance's anonymous participation settings"
|
description: "The instance's anonymous participation settings"
|
||||||
)
|
)
|
||||||
|
@ -172,6 +183,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Instance anonymous participation configuration
|
Instance anonymous participation configuration
|
||||||
"""
|
"""
|
||||||
object :anonymous_participation do
|
object :anonymous_participation do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:allowed, :boolean, description: "Whether anonymous participations are allowed")
|
field(:allowed, :boolean, description: "Whether anonymous participations are allowed")
|
||||||
|
|
||||||
field(:validation, :anonymous_participation_validation,
|
field(:validation, :anonymous_participation_validation,
|
||||||
|
@ -183,6 +195,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Instance anonymous participation validation configuration
|
Instance anonymous participation validation configuration
|
||||||
"""
|
"""
|
||||||
object :anonymous_participation_validation do
|
object :anonymous_participation_validation do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
|
||||||
field(:email, :anonymous_participation_validation_email,
|
field(:email, :anonymous_participation_validation_email,
|
||||||
description: "The policy to validate anonymous participations by email"
|
description: "The policy to validate anonymous participations by email"
|
||||||
)
|
)
|
||||||
|
@ -196,6 +210,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Instance anonymous participation with validation by email configuration
|
Instance anonymous participation with validation by email configuration
|
||||||
"""
|
"""
|
||||||
object :anonymous_participation_validation_email do
|
object :anonymous_participation_validation_email do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
|
||||||
field(:enabled, :boolean,
|
field(:enabled, :boolean,
|
||||||
description: "Whether anonymous participation validation by email is enabled"
|
description: "Whether anonymous participation validation by email is enabled"
|
||||||
)
|
)
|
||||||
|
@ -209,6 +225,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Instance anonymous participation with validation by captcha configuration
|
Instance anonymous participation with validation by captcha configuration
|
||||||
"""
|
"""
|
||||||
object :anonymous_participation_validation_captcha do
|
object :anonymous_participation_validation_captcha do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
|
||||||
field(:enabled, :boolean,
|
field(:enabled, :boolean,
|
||||||
description: "Whether anonymous participation validation by captcha is enabled"
|
description: "Whether anonymous participation validation by captcha is enabled"
|
||||||
)
|
)
|
||||||
|
@ -218,6 +236,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Instance anonymous event creation configuration
|
Instance anonymous event creation configuration
|
||||||
"""
|
"""
|
||||||
object :anonymous_event_creation do
|
object :anonymous_event_creation do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:allowed, :boolean, description: "Whether anonymous event creation is enabled")
|
field(:allowed, :boolean, description: "Whether anonymous event creation is enabled")
|
||||||
|
|
||||||
field(:validation, :anonymous_event_creation_validation,
|
field(:validation, :anonymous_event_creation_validation,
|
||||||
|
@ -229,6 +248,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Instance anonymous event creation validation configuration
|
Instance anonymous event creation validation configuration
|
||||||
"""
|
"""
|
||||||
object :anonymous_event_creation_validation do
|
object :anonymous_event_creation_validation do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
|
||||||
field(:email, :anonymous_event_creation_validation_email,
|
field(:email, :anonymous_event_creation_validation_email,
|
||||||
description: "The policy to validate anonymous event creations by email"
|
description: "The policy to validate anonymous event creations by email"
|
||||||
)
|
)
|
||||||
|
@ -242,6 +263,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Instance anonymous event creation email validation configuration
|
Instance anonymous event creation email validation configuration
|
||||||
"""
|
"""
|
||||||
object :anonymous_event_creation_validation_email do
|
object :anonymous_event_creation_validation_email do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
|
||||||
field(:enabled, :boolean,
|
field(:enabled, :boolean,
|
||||||
description: "Whether anonymous event creation with email validation is enabled"
|
description: "Whether anonymous event creation with email validation is enabled"
|
||||||
)
|
)
|
||||||
|
@ -255,6 +278,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Instance anonymous event creation captcha validation configuration
|
Instance anonymous event creation captcha validation configuration
|
||||||
"""
|
"""
|
||||||
object :anonymous_event_creation_validation_captcha do
|
object :anonymous_event_creation_validation_captcha do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
|
||||||
field(:enabled, :boolean,
|
field(:enabled, :boolean,
|
||||||
description: "Whether anonymous event creation with validation by captcha is enabled"
|
description: "Whether anonymous event creation with validation by captcha is enabled"
|
||||||
)
|
)
|
||||||
|
@ -264,6 +289,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Instance anonymous reports
|
Instance anonymous reports
|
||||||
"""
|
"""
|
||||||
object :anonymous_reports do
|
object :anonymous_reports do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:allowed, :boolean, description: "Whether anonymous reports are allowed")
|
field(:allowed, :boolean, description: "Whether anonymous reports are allowed")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -271,6 +297,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
A resource provider details
|
A resource provider details
|
||||||
"""
|
"""
|
||||||
object :resource_provider do
|
object :resource_provider do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:type, :string, description: "The resource provider's type")
|
field(:type, :string, description: "The resource provider's type")
|
||||||
field(:endpoint, :string, description: "The resource provider's endpoint")
|
field(:endpoint, :string, description: "The resource provider's endpoint")
|
||||||
field(:software, :string, description: "The resource provider's software")
|
field(:software, :string, description: "The resource provider's software")
|
||||||
|
@ -280,17 +307,22 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
The instance's features
|
The instance's features
|
||||||
"""
|
"""
|
||||||
object :features do
|
object :features do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:groups, :boolean, description: "Whether groups are activated on this instance")
|
field(:groups, :boolean, description: "Whether groups are activated on this instance")
|
||||||
|
|
||||||
field(:event_creation, :boolean,
|
field(:event_creation, :boolean,
|
||||||
description: "Whether event creation is allowed on this instance"
|
description: "Whether event creation is allowed on this instance"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
field(:antispam, :boolean, description: "Whether anti-spam is activated on this instance")
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc """
|
@desc """
|
||||||
The instance's restrictions
|
The instance's restrictions
|
||||||
"""
|
"""
|
||||||
object :restrictions do
|
object :restrictions do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
|
||||||
field(:only_admin_can_create_groups, :boolean,
|
field(:only_admin_can_create_groups, :boolean,
|
||||||
description: "Whether groups creation is allowed only for admin, not for all users"
|
description: "Whether groups creation is allowed only for admin, not for all users"
|
||||||
)
|
)
|
||||||
|
@ -304,6 +336,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
The instance's auth configuration
|
The instance's auth configuration
|
||||||
"""
|
"""
|
||||||
object :auth do
|
object :auth do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:ldap, :boolean, description: "Whether or not LDAP auth is enabled")
|
field(:ldap, :boolean, description: "Whether or not LDAP auth is enabled")
|
||||||
field(:database_login, :boolean, description: "Whether or not database login is enabled")
|
field(:database_login, :boolean, description: "Whether or not database login is enabled")
|
||||||
field(:oauth_providers, list_of(:oauth_provider), description: "List of oauth providers")
|
field(:oauth_providers, list_of(:oauth_provider), description: "List of oauth providers")
|
||||||
|
@ -313,6 +346,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
An oAuth Provider
|
An oAuth Provider
|
||||||
"""
|
"""
|
||||||
object :oauth_provider do
|
object :oauth_provider do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:id, :string, description: "The provider ID")
|
field(:id, :string, description: "The provider ID")
|
||||||
field(:label, :string, description: "The label for the auth provider")
|
field(:label, :string, description: "The label for the auth provider")
|
||||||
end
|
end
|
||||||
|
@ -321,21 +355,25 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
An upload limits configuration
|
An upload limits configuration
|
||||||
"""
|
"""
|
||||||
object :upload_limits do
|
object :upload_limits do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:default, :integer, description: "The default limitation, in bytes")
|
field(:default, :integer, description: "The default limitation, in bytes")
|
||||||
field(:avatar, :integer, description: "The avatar limitation, in bytes")
|
field(:avatar, :integer, description: "The avatar limitation, in bytes")
|
||||||
field(:banner, :integer, description: "The banner limitation, in bytes")
|
field(:banner, :integer, description: "The banner limitation, in bytes")
|
||||||
end
|
end
|
||||||
|
|
||||||
object :instance_feeds do
|
object :instance_feeds do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:enabled, :boolean, description: "Whether the instance-wide feeds are enabled")
|
field(:enabled, :boolean, description: "Whether the instance-wide feeds are enabled")
|
||||||
end
|
end
|
||||||
|
|
||||||
object :web_push do
|
object :web_push do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:enabled, :boolean, description: "Whether the WebPush feature is enabled")
|
field(:enabled, :boolean, description: "Whether the WebPush feature is enabled")
|
||||||
field(:public_key, :string, description: "The server's public WebPush VAPID key")
|
field(:public_key, :string, description: "The server's public WebPush VAPID key")
|
||||||
end
|
end
|
||||||
|
|
||||||
object :analytics do
|
object :analytics do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:id, :string, description: "ID of the analytics service")
|
field(:id, :string, description: "ID of the analytics service")
|
||||||
field(:enabled, :boolean, description: "Whether the service is activated or not")
|
field(:enabled, :boolean, description: "Whether the service is activated or not")
|
||||||
|
|
||||||
|
@ -352,16 +390,19 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
end
|
end
|
||||||
|
|
||||||
object :analytics_configuration do
|
object :analytics_configuration do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:key, :string, description: "The key for the analytics configuration element")
|
field(:key, :string, description: "The key for the analytics configuration element")
|
||||||
field(:value, :string, description: "The value for the analytics configuration element")
|
field(:value, :string, description: "The value for the analytics configuration element")
|
||||||
field(:type, :analytics_configuration_type, description: "The analytics configuration type")
|
field(:type, :analytics_configuration_type, description: "The analytics configuration type")
|
||||||
end
|
end
|
||||||
|
|
||||||
object :search_settings do
|
object :search_settings do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:global, :global_search_settings, description: "The instance's global search settings")
|
field(:global, :global_search_settings, description: "The instance's global search settings")
|
||||||
end
|
end
|
||||||
|
|
||||||
object :global_search_settings do
|
object :global_search_settings do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:is_enabled, :boolean, description: "Whether global search is enabled")
|
field(:is_enabled, :boolean, description: "Whether global search is enabled")
|
||||||
field(:is_default, :boolean, description: "Whether global search is the default")
|
field(:is_default, :boolean, description: "Whether global search is the default")
|
||||||
end
|
end
|
||||||
|
@ -370,6 +411,8 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Export formats configuration
|
Export formats configuration
|
||||||
"""
|
"""
|
||||||
object :export_formats do
|
object :export_formats do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
|
||||||
field(:event_participants, list_of(:string),
|
field(:event_participants, list_of(:string),
|
||||||
description: "The list of formats the event participants can be exported to"
|
description: "The list of formats the event participants can be exported to"
|
||||||
)
|
)
|
||||||
|
@ -379,6 +422,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
Event categories list configuration
|
Event categories list configuration
|
||||||
"""
|
"""
|
||||||
object :event_category_option do
|
object :event_category_option do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:id, :string, description: "The ID of the event category")
|
field(:id, :string, description: "The ID of the event category")
|
||||||
field(:label, :string, description: "The translated name of the event category")
|
field(:label, :string, description: "The translated name of the event category")
|
||||||
end
|
end
|
||||||
|
@ -386,6 +430,7 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||||
object :config_queries do
|
object :config_queries do
|
||||||
@desc "Get the instance config"
|
@desc "Get the instance config"
|
||||||
field :config, :config do
|
field :config, :config do
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&Config.get_config/3)
|
resolve(&Config.get_config/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,6 +11,7 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
|
||||||
|
|
||||||
@desc "A comment"
|
@desc "A comment"
|
||||||
object :comment do
|
object :comment do
|
||||||
|
meta(:authorize, :all)
|
||||||
interfaces([:action_log_object, :activity_object])
|
interfaces([:action_log_object, :activity_object])
|
||||||
field(:id, :id, description: "Internal ID for this comment")
|
field(:id, :id, description: "Internal ID for this comment")
|
||||||
field(:uuid, :uuid, description: "An UUID for this comment")
|
field(:uuid, :uuid, description: "An UUID for this comment")
|
||||||
|
@ -73,6 +74,7 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
|
||||||
|
|
||||||
@desc "A paginated list of comments"
|
@desc "A paginated list of comments"
|
||||||
object :paginated_comment_list do
|
object :paginated_comment_list do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:elements, list_of(:comment), description: "A list of comments")
|
field(:elements, list_of(:comment), description: "A list of comments")
|
||||||
field(:total, :integer, description: "The total number of comments in the list")
|
field(:total, :integer, description: "The total number of comments in the list")
|
||||||
end
|
end
|
||||||
|
@ -81,6 +83,7 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
|
||||||
@desc "Get replies for thread"
|
@desc "Get replies for thread"
|
||||||
field :thread, type: list_of(:comment) do
|
field :thread, type: list_of(:comment) do
|
||||||
arg(:id, non_null(:id), description: "The comment ID")
|
arg(:id, non_null(:id), description: "The comment ID")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&Comment.get_thread/3)
|
resolve(&Comment.get_thread/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -95,6 +98,13 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
|
||||||
|
|
||||||
arg(:is_announcement, :boolean, description: "Should this comment be announced to everyone?")
|
arg(:is_announcement, :boolean, description: "Should this comment be announced to everyone?")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Discussions.Comment,
|
||||||
|
rule: :"write:comment:create",
|
||||||
|
args: %{event_id: :event_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Comment.create_comment/3)
|
resolve(&Comment.create_comment/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -106,6 +116,13 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
|
||||||
|
|
||||||
arg(:is_announcement, :boolean, description: "Should this comment be announced to everyone?")
|
arg(:is_announcement, :boolean, description: "Should this comment be announced to everyone?")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Discussions.Comment,
|
||||||
|
rule: :"write:comment:update",
|
||||||
|
args: %{id: :comment_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Comment.update_comment/3)
|
resolve(&Comment.update_comment/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -113,6 +130,13 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
|
||||||
field :delete_comment, type: :comment do
|
field :delete_comment, type: :comment do
|
||||||
arg(:comment_id, non_null(:id), description: "The comment ID")
|
arg(:comment_id, non_null(:id), description: "The comment ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: [:user, :moderator],
|
||||||
|
scope: Mobilizon.Discussions.Comment,
|
||||||
|
rule: :"write:comment:delete",
|
||||||
|
args: %{id: :comment_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Comment.delete_comment/3)
|
resolve(&Comment.delete_comment/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,6 +11,7 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.DiscussionType do
|
||||||
|
|
||||||
@desc "A discussion"
|
@desc "A discussion"
|
||||||
object :discussion do
|
object :discussion do
|
||||||
|
meta(:authorize, :user)
|
||||||
interfaces([:activity_object])
|
interfaces([:activity_object])
|
||||||
field(:id, :id, description: "Internal ID for this discussion")
|
field(:id, :id, description: "Internal ID for this discussion")
|
||||||
field(:title, :string, description: "The title for this discussion")
|
field(:title, :string, description: "The title for this discussion")
|
||||||
|
@ -36,6 +37,7 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.DiscussionType do
|
||||||
|
|
||||||
@desc "A paginated list of discussions"
|
@desc "A paginated list of discussions"
|
||||||
object :paginated_discussion_list do
|
object :paginated_discussion_list do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:elements, list_of(:discussion), description: "A list of discussion")
|
field(:elements, list_of(:discussion), description: "A list of discussion")
|
||||||
field(:total, :integer, description: "The total number of discussions in the list")
|
field(:total, :integer, description: "The total number of discussions in the list")
|
||||||
end
|
end
|
||||||
|
@ -45,6 +47,13 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.DiscussionType do
|
||||||
field :discussion, type: :discussion do
|
field :discussion, type: :discussion do
|
||||||
arg(:id, :id, description: "The discussion's ID")
|
arg(:id, :id, description: "The discussion's ID")
|
||||||
arg(:slug, :string, description: "The discussion's slug")
|
arg(:slug, :string, description: "The discussion's slug")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Discussions.Discussion,
|
||||||
|
rule: :"read:group:discussions"
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Discussion.get_discussion/3)
|
resolve(&Discussion.get_discussion/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -56,6 +65,13 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.DiscussionType do
|
||||||
arg(:text, non_null(:string), description: "The discussion's first comment body")
|
arg(:text, non_null(:string), description: "The discussion's first comment body")
|
||||||
arg(:actor_id, non_null(:id), description: "The discussion's group ID")
|
arg(:actor_id, non_null(:id), description: "The discussion's group ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Discussions.Discussion,
|
||||||
|
rule: :"write:group:discussion:create",
|
||||||
|
args: %{actor_id: :actor_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Discussion.create_discussion/3)
|
resolve(&Discussion.create_discussion/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -63,6 +79,14 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.DiscussionType do
|
||||||
field :reply_to_discussion, type: :discussion do
|
field :reply_to_discussion, type: :discussion do
|
||||||
arg(:discussion_id, non_null(:id), description: "The discussion's ID")
|
arg(:discussion_id, non_null(:id), description: "The discussion's ID")
|
||||||
arg(:text, non_null(:string), description: "The discussion's reply body")
|
arg(:text, non_null(:string), description: "The discussion's reply body")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Discussions.Discussion,
|
||||||
|
rule: :"write:group:discussion:update",
|
||||||
|
args: %{id: :discussion_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Discussion.reply_to_discussion/3)
|
resolve(&Discussion.reply_to_discussion/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -70,6 +94,14 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.DiscussionType do
|
||||||
field :update_discussion, type: :discussion do
|
field :update_discussion, type: :discussion do
|
||||||
arg(:title, non_null(:string), description: "The updated discussion's title")
|
arg(:title, non_null(:string), description: "The updated discussion's title")
|
||||||
arg(:discussion_id, non_null(:id), description: "The discussion's ID")
|
arg(:discussion_id, non_null(:id), description: "The discussion's ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Discussions.Discussion,
|
||||||
|
rule: :"write:group:discussion:update",
|
||||||
|
args: %{id: :discussion_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Discussion.update_discussion/3)
|
resolve(&Discussion.update_discussion/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -77,6 +109,13 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.DiscussionType do
|
||||||
field :delete_discussion, type: :discussion do
|
field :delete_discussion, type: :discussion do
|
||||||
arg(:discussion_id, non_null(:id), description: "The discussion's ID")
|
arg(:discussion_id, non_null(:id), description: "The discussion's ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Discussions.Discussion,
|
||||||
|
rule: :"write:group:discussion:delete",
|
||||||
|
args: %{id: :discussion_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Discussion.delete_discussion/3)
|
resolve(&Discussion.delete_discussion/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,6 +17,8 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
||||||
|
|
||||||
@desc "An event"
|
@desc "An event"
|
||||||
object :event do
|
object :event do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
meta(:scope_field?, true)
|
||||||
interfaces([:action_log_object, :interactable, :activity_object, :event_search_result])
|
interfaces([:action_log_object, :interactable, :activity_object, :event_search_result])
|
||||||
field(:id, :id, description: "Internal ID for this event")
|
field(:id, :id, description: "Internal ID for this event")
|
||||||
field(:uuid, :uuid, description: "The Event UUID")
|
field(:uuid, :uuid, description: "The Event UUID")
|
||||||
|
@ -61,10 +63,9 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
||||||
description: "The event's organizer (as a person)"
|
description: "The event's organizer (as a person)"
|
||||||
)
|
)
|
||||||
|
|
||||||
field(:tags, list_of(:tag),
|
field(:tags, list_of(:tag), description: "The event's tags") do
|
||||||
resolve: &Tag.list_tags_for_event/3,
|
resolve(&Tag.list_tags_for_event/3)
|
||||||
description: "The event's tags"
|
end
|
||||||
)
|
|
||||||
|
|
||||||
field(:category, :event_category, description: "The event's category")
|
field(:category, :event_category, description: "The event's category")
|
||||||
|
|
||||||
|
@ -75,7 +76,10 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
||||||
resolve: &Event.stats_participants/3
|
resolve: &Event.stats_participants/3
|
||||||
)
|
)
|
||||||
|
|
||||||
field(:participants, :paginated_participant_list, description: "The event's participants") do
|
field(:participants, :paginated_participant_list,
|
||||||
|
description: "The event's participants",
|
||||||
|
meta: [private: true, rule: :"read:event:participants"]
|
||||||
|
) do
|
||||||
arg(:page, :integer,
|
arg(:page, :integer,
|
||||||
default_value: 1,
|
default_value: 1,
|
||||||
description: "The page in the paginated participants list"
|
description: "The page in the paginated participants list"
|
||||||
|
@ -134,12 +138,14 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
||||||
|
|
||||||
@desc "A paginated list of events"
|
@desc "A paginated list of events"
|
||||||
object :paginated_event_list do
|
object :paginated_event_list do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:elements, list_of(:event), description: "A list of events")
|
field(:elements, list_of(:event), description: "A list of events")
|
||||||
field(:total, :integer, description: "The total number of events in the list")
|
field(:total, :integer, description: "The total number of events in the list")
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Participation statistics"
|
@desc "Participation statistics"
|
||||||
object :participant_stats do
|
object :participant_stats do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:going, :integer, description: "The number of approved participants")
|
field(:going, :integer, description: "The number of approved participants")
|
||||||
field(:not_approved, :integer, description: "The number of not approved participants")
|
field(:not_approved, :integer, description: "The number of not approved participants")
|
||||||
field(:not_confirmed, :integer, description: "The number of not confirmed participants")
|
field(:not_confirmed, :integer, description: "The number of not confirmed participants")
|
||||||
|
@ -158,6 +164,7 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
||||||
An event offer
|
An event offer
|
||||||
"""
|
"""
|
||||||
object :event_offer do
|
object :event_offer do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:price, :float, description: "The price amount for this offer")
|
field(:price, :float, description: "The price amount for this offer")
|
||||||
field(:price_currency, :string, description: "The currency for this price offer")
|
field(:price_currency, :string, description: "The currency for this price offer")
|
||||||
field(:url, :string, description: "The URL to access to this offer")
|
field(:url, :string, description: "The URL to access to this offer")
|
||||||
|
@ -167,6 +174,7 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
||||||
An event participation condition
|
An event participation condition
|
||||||
"""
|
"""
|
||||||
object :event_participation_condition do
|
object :event_participation_condition do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:title, :string, description: "The title for this condition")
|
field(:title, :string, description: "The title for this condition")
|
||||||
field(:content, :string, description: "The content for this condition")
|
field(:content, :string, description: "The content for this condition")
|
||||||
field(:url, :string, description: "The URL to access this condition")
|
field(:url, :string, description: "The URL to access this condition")
|
||||||
|
@ -201,6 +209,8 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
||||||
Event options
|
Event options
|
||||||
"""
|
"""
|
||||||
object :event_options do
|
object :event_options do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
|
||||||
field(:maximum_attendee_capacity, :integer,
|
field(:maximum_attendee_capacity, :integer,
|
||||||
description: "The maximum attendee capacity for this event"
|
description: "The maximum attendee capacity for this event"
|
||||||
)
|
)
|
||||||
|
@ -307,6 +317,7 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
||||||
end
|
end
|
||||||
|
|
||||||
object :event_metadata do
|
object :event_metadata do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:key, :string, description: "The key for the metadata")
|
field(:key, :string, description: "The key for the metadata")
|
||||||
field(:title, :string, description: "The title for the metadata")
|
field(:title, :string, description: "The title for the metadata")
|
||||||
field(:value, :string, description: "The value for the metadata")
|
field(:value, :string, description: "The value for the metadata")
|
||||||
|
@ -350,12 +361,15 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
||||||
description: "Direction for the sort"
|
description: "Direction for the sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
|
|
||||||
resolve(&Event.list_events/3)
|
resolve(&Event.list_events/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Get an event by uuid"
|
@desc "Get an event by uuid"
|
||||||
field :event, :event do
|
field :event, :event do
|
||||||
arg(:uuid, non_null(:uuid), description: "The event's UUID")
|
arg(:uuid, non_null(:uuid), description: "The event's UUID")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&Event.find_event/3)
|
resolve(&Event.find_event/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -416,6 +430,13 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
||||||
arg(:contacts, list_of(:contact), default_value: [], description: "The events contacts")
|
arg(:contacts, list_of(:contact), default_value: [], description: "The events contacts")
|
||||||
arg(:language, :string, description: "The event language", default_value: "und")
|
arg(:language, :string, description: "The event language", default_value: "und")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Events.Event,
|
||||||
|
rule: :"write:event:create",
|
||||||
|
args: %{organizer_actor_id: :organizer_actor_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Event.create_event/3)
|
resolve(&Event.create_event/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -460,6 +481,13 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
||||||
arg(:contacts, list_of(:contact), default_value: [], description: "The events contacts")
|
arg(:contacts, list_of(:contact), default_value: [], description: "The events contacts")
|
||||||
arg(:language, :string, description: "The event language", default_value: "und")
|
arg(:language, :string, description: "The event language", default_value: "und")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Events.Event,
|
||||||
|
args: %{id: :event_id},
|
||||||
|
rule: :"write:event:update"
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Event.update_event/3)
|
resolve(&Event.update_event/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -467,6 +495,13 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
||||||
field :delete_event, :deleted_object do
|
field :delete_event, :deleted_object do
|
||||||
arg(:event_id, non_null(:id), description: "The event ID to delete")
|
arg(:event_id, non_null(:id), description: "The event ID to delete")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: [:user, :moderator, :administrator],
|
||||||
|
scope: Mobilizon.Events.Event,
|
||||||
|
rule: :"write:event:delete",
|
||||||
|
args: %{id: :event_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Event.delete_event/3)
|
resolve(&Event.delete_event/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,6 +17,8 @@ defmodule Mobilizon.GraphQL.Schema.Events.FeedTokenType do
|
||||||
or an Atom feed for just a profile.
|
or an Atom feed for just a profile.
|
||||||
"""
|
"""
|
||||||
object :feed_token do
|
object :feed_token do
|
||||||
|
meta(:authorize, :user)
|
||||||
|
|
||||||
field(
|
field(
|
||||||
:actor,
|
:actor,
|
||||||
:actor,
|
:actor,
|
||||||
|
@ -36,6 +38,7 @@ defmodule Mobilizon.GraphQL.Schema.Events.FeedTokenType do
|
||||||
|
|
||||||
@desc "Represents a deleted feed_token"
|
@desc "Represents a deleted feed_token"
|
||||||
object :deleted_feed_token do
|
object :deleted_feed_token do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:user, :deleted_object, description: "The user that owned the deleted feed token")
|
field(:user, :deleted_object, description: "The user that owned the deleted feed token")
|
||||||
field(:actor, :deleted_object, description: "The actor that owned the deleted feed token")
|
field(:actor, :deleted_object, description: "The actor that owned the deleted feed token")
|
||||||
end
|
end
|
||||||
|
@ -45,6 +48,13 @@ defmodule Mobilizon.GraphQL.Schema.Events.FeedTokenType do
|
||||||
field :create_feed_token, :feed_token do
|
field :create_feed_token, :feed_token do
|
||||||
arg(:actor_id, :id, description: "The actor ID for the feed token")
|
arg(:actor_id, :id, description: "The actor ID for the feed token")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Events.FeedToken,
|
||||||
|
rule: :"write:profile:feed_token:create",
|
||||||
|
args: %{}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&FeedToken.create_feed_token/3)
|
resolve(&FeedToken.create_feed_token/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -52,6 +62,13 @@ defmodule Mobilizon.GraphQL.Schema.Events.FeedTokenType do
|
||||||
field :delete_feed_token, :deleted_feed_token do
|
field :delete_feed_token, :deleted_feed_token do
|
||||||
arg(:token, non_null(:string), description: "The token to delete")
|
arg(:token, non_null(:string), description: "The token to delete")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Events.FeedToken,
|
||||||
|
rule: :"write:feed_token:delete",
|
||||||
|
args: %{token: :token}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&FeedToken.delete_feed_token/3)
|
resolve(&FeedToken.delete_feed_token/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,6 +12,7 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
|
||||||
|
|
||||||
@desc "Represents a participant to an event"
|
@desc "Represents a participant to an event"
|
||||||
object :participant do
|
object :participant do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:id, :id, description: "The participation ID")
|
field(:id, :id, description: "The participation ID")
|
||||||
|
|
||||||
field(
|
field(
|
||||||
|
@ -41,6 +42,8 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
|
||||||
Metadata about a participant
|
Metadata about a participant
|
||||||
"""
|
"""
|
||||||
object :participant_metadata do
|
object :participant_metadata do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
|
||||||
field(:cancellation_token, :string,
|
field(:cancellation_token, :string,
|
||||||
description: "The eventual token to leave an event when user is anonymous"
|
description: "The eventual token to leave an event when user is anonymous"
|
||||||
)
|
)
|
||||||
|
@ -53,6 +56,7 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
|
||||||
A paginated list of participants
|
A paginated list of participants
|
||||||
"""
|
"""
|
||||||
object :paginated_participant_list do
|
object :paginated_participant_list do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:elements, list_of(:participant), description: "A list of participants")
|
field(:elements, list_of(:participant), description: "A list of participants")
|
||||||
field(:total, :integer, description: "The total number of participants in the list")
|
field(:total, :integer, description: "The total number of participants in the list")
|
||||||
end
|
end
|
||||||
|
@ -78,6 +82,7 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
|
||||||
|
|
||||||
@desc "Represents a deleted participant"
|
@desc "Represents a deleted participant"
|
||||||
object :deleted_participant do
|
object :deleted_participant do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:id, :id, description: "The participant ID")
|
field(:id, :id, description: "The participant ID")
|
||||||
field(:event, :deleted_object, description: "The participant's event")
|
field(:event, :deleted_object, description: "The participant's event")
|
||||||
field(:actor, :deleted_object, description: "The participant's actor")
|
field(:actor, :deleted_object, description: "The participant's actor")
|
||||||
|
@ -92,7 +97,7 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
|
||||||
arg(:message, :string, description: "The anonymous participant's message")
|
arg(:message, :string, description: "The anonymous participant's message")
|
||||||
arg(:locale, :string, description: "The anonymous participant's locale")
|
arg(:locale, :string, description: "The anonymous participant's locale")
|
||||||
arg(:timezone, :string, description: "The anonymous participant's timezone")
|
arg(:timezone, :string, description: "The anonymous participant's timezone")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all, rule: :"write:participation")
|
||||||
resolve(&Participant.actor_join_event/3)
|
resolve(&Participant.actor_join_event/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -101,7 +106,7 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
|
||||||
arg(:event_id, non_null(:id), description: "The event ID the participant left")
|
arg(:event_id, non_null(:id), description: "The event ID the participant left")
|
||||||
arg(:actor_id, non_null(:id), description: "The actor ID for the participant")
|
arg(:actor_id, non_null(:id), description: "The actor ID for the participant")
|
||||||
arg(:token, :string, description: "The anonymous participant participation token")
|
arg(:token, :string, description: "The anonymous participant participation token")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all, rule: :"write:participation")
|
||||||
resolve(&Participant.actor_leave_event/3)
|
resolve(&Participant.actor_leave_event/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -110,12 +115,19 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
|
||||||
arg(:id, non_null(:id), description: "The participant ID")
|
arg(:id, non_null(:id), description: "The participant ID")
|
||||||
arg(:role, non_null(:participant_role_enum), description: "The participant new role")
|
arg(:role, non_null(:participant_role_enum), description: "The participant new role")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Events.Participant,
|
||||||
|
rule: :"write:participation"
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Participant.update_participation/3)
|
resolve(&Participant.update_participation/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Confirm a participation"
|
@desc "Confirm a participation"
|
||||||
field :confirm_participation, :participant do
|
field :confirm_participation, :participant do
|
||||||
arg(:confirmation_token, non_null(:string), description: "The participation token")
|
arg(:confirmation_token, non_null(:string), description: "The participation token")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all, rule: :"write:participation")
|
||||||
resolve(&Participant.confirm_participation_from_token/3)
|
resolve(&Participant.confirm_participation_from_token/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -131,6 +143,14 @@ defmodule Mobilizon.GraphQL.Schema.Events.ParticipantType do
|
||||||
)
|
)
|
||||||
|
|
||||||
arg(:format, :export_format_enum, description: "The format in which to return the file")
|
arg(:format, :export_format_enum, description: "The format in which to return the file")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Events.Event,
|
||||||
|
rule: :"read:event:participants:export",
|
||||||
|
args: %{id: :event_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Participant.export_event_participants/3)
|
resolve(&Participant.export_event_participants/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,12 +6,14 @@ defmodule Mobilizon.GraphQL.Schema.FollowedGroupActivityType do
|
||||||
|
|
||||||
@desc "A paginated list of follow group events"
|
@desc "A paginated list of follow group events"
|
||||||
object :paginated_followed_group_events do
|
object :paginated_followed_group_events do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:elements, list_of(:followed_group_event), description: "A list of follow group events")
|
field(:elements, list_of(:followed_group_event), description: "A list of follow group events")
|
||||||
field(:total, :integer, description: "The total number of follow group events in the list")
|
field(:total, :integer, description: "The total number of follow group events in the list")
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "A follow group event"
|
@desc "A follow group event"
|
||||||
object :followed_group_event do
|
object :followed_group_event do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:user, :user)
|
field(:user, :user)
|
||||||
field(:profile, :person)
|
field(:profile, :person)
|
||||||
field(:group, :group)
|
field(:group, :group)
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Mobilizon.GraphQL.Schema.MediaType do
|
||||||
|
|
||||||
@desc "A media"
|
@desc "A media"
|
||||||
object :media do
|
object :media do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:id, :id, description: "The media's ID")
|
field(:id, :id, description: "The media's ID")
|
||||||
field(:alt, :string, description: "The media's alternative text")
|
field(:alt, :string, description: "The media's alternative text")
|
||||||
field(:name, :string, description: "The media's name")
|
field(:name, :string, description: "The media's name")
|
||||||
|
@ -21,6 +22,7 @@ defmodule Mobilizon.GraphQL.Schema.MediaType do
|
||||||
A paginated list of medias
|
A paginated list of medias
|
||||||
"""
|
"""
|
||||||
object :paginated_media_list do
|
object :paginated_media_list do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:elements, list_of(:media), description: "The list of medias")
|
field(:elements, list_of(:media), description: "The list of medias")
|
||||||
field(:total, :integer, description: "The total number of medias in the list")
|
field(:total, :integer, description: "The total number of medias in the list")
|
||||||
end
|
end
|
||||||
|
@ -29,6 +31,7 @@ defmodule Mobilizon.GraphQL.Schema.MediaType do
|
||||||
Some metadata associated with a media
|
Some metadata associated with a media
|
||||||
"""
|
"""
|
||||||
object :media_metadata do
|
object :media_metadata do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:width, :integer, description: "The media width (if a picture)")
|
field(:width, :integer, description: "The media width (if a picture)")
|
||||||
field(:height, :integer, description: "The media width (if a height)")
|
field(:height, :integer, description: "The media width (if a height)")
|
||||||
field(:blurhash, :string, description: "The media blurhash (if a picture")
|
field(:blurhash, :string, description: "The media blurhash (if a picture")
|
||||||
|
@ -54,6 +57,7 @@ defmodule Mobilizon.GraphQL.Schema.MediaType do
|
||||||
@desc "Get a media"
|
@desc "Get a media"
|
||||||
field :media, :media do
|
field :media, :media do
|
||||||
arg(:id, non_null(:id), description: "The media ID")
|
arg(:id, non_null(:id), description: "The media ID")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&Media.media/3)
|
resolve(&Media.media/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -64,6 +68,15 @@ defmodule Mobilizon.GraphQL.Schema.MediaType do
|
||||||
arg(:name, non_null(:string), description: "The media's name")
|
arg(:name, non_null(:string), description: "The media's name")
|
||||||
arg(:alt, :string, description: "The media's alternative text")
|
arg(:alt, :string, description: "The media's alternative text")
|
||||||
arg(:file, non_null(:upload), description: "The media file")
|
arg(:file, non_null(:upload), description: "The media file")
|
||||||
|
arg(:actor_id, :id, description: "The actor that uploads the media")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Medias.Media,
|
||||||
|
rule: :"write:media:upload",
|
||||||
|
args: %{}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Media.upload_media/3)
|
resolve(&Media.upload_media/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -72,6 +85,13 @@ defmodule Mobilizon.GraphQL.Schema.MediaType do
|
||||||
"""
|
"""
|
||||||
field :remove_media, :deleted_object do
|
field :remove_media, :deleted_object do
|
||||||
arg(:id, non_null(:id), description: "The media's ID")
|
arg(:id, non_null(:id), description: "The media's ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Medias.Media,
|
||||||
|
rule: :"write:media:remove"
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Media.remove_media/3)
|
resolve(&Media.remove_media/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Mobilizon.GraphQL.Schema.PostType do
|
||||||
|
|
||||||
@desc "A post"
|
@desc "A post"
|
||||||
object :post do
|
object :post do
|
||||||
|
meta(:authorize, :all)
|
||||||
interfaces([:activity_object])
|
interfaces([:activity_object])
|
||||||
field(:id, :id, description: "The post's ID")
|
field(:id, :id, description: "The post's ID")
|
||||||
field(:title, :string, description: "The post's title")
|
field(:title, :string, description: "The post's title")
|
||||||
|
@ -22,21 +23,20 @@ defmodule Mobilizon.GraphQL.Schema.PostType do
|
||||||
field(:updated_at, :datetime, description: "The post's last update date")
|
field(:updated_at, :datetime, description: "The post's last update date")
|
||||||
field(:language, :string, description: "The post language")
|
field(:language, :string, description: "The post language")
|
||||||
|
|
||||||
field(:tags, list_of(:tag),
|
field(:tags, list_of(:tag), description: "The post's tags") do
|
||||||
resolve: &Tag.list_tags_for_post/3,
|
resolve(&Tag.list_tags_for_post/3)
|
||||||
description: "The post's tags"
|
end
|
||||||
)
|
|
||||||
|
|
||||||
field(:picture, :media,
|
field(:picture, :media, description: "The posts's media") do
|
||||||
description: "The posts's media",
|
resolve(&Media.media/3)
|
||||||
resolve: &Media.media/3
|
end
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc """
|
@desc """
|
||||||
A paginated list of posts
|
A paginated list of posts
|
||||||
"""
|
"""
|
||||||
object :paginated_post_list do
|
object :paginated_post_list do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:elements, list_of(:post), description: "A list of posts")
|
field(:elements, list_of(:post), description: "A list of posts")
|
||||||
field(:total, :integer, description: "The total number of posts in the list")
|
field(:total, :integer, description: "The total number of posts in the list")
|
||||||
end
|
end
|
||||||
|
@ -56,6 +56,7 @@ defmodule Mobilizon.GraphQL.Schema.PostType do
|
||||||
@desc "Get a post"
|
@desc "Get a post"
|
||||||
field :post, :post do
|
field :post, :post do
|
||||||
arg(:slug, non_null(:string), description: "The post's slug")
|
arg(:slug, non_null(:string), description: "The post's slug")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&Post.get_post/3)
|
resolve(&Post.get_post/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -84,6 +85,13 @@ defmodule Mobilizon.GraphQL.Schema.PostType do
|
||||||
"The banner for the post, either as an object or directly the ID of an existing media"
|
"The banner for the post, either as an object or directly the ID of an existing media"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Posts.Post,
|
||||||
|
rule: :"write:group:post:create",
|
||||||
|
args: %{}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Post.create_post/3)
|
resolve(&Post.create_post/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -108,12 +116,25 @@ defmodule Mobilizon.GraphQL.Schema.PostType do
|
||||||
"The banner for the post, either as an object or directly the ID of an existing media"
|
"The banner for the post, either as an object or directly the ID of an existing media"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Posts.Post,
|
||||||
|
rule: :"write:group:post:update"
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Post.update_post/3)
|
resolve(&Post.update_post/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Delete a post"
|
@desc "Delete a post"
|
||||||
field :delete_post, :deleted_object do
|
field :delete_post, :deleted_object do
|
||||||
arg(:id, non_null(:id), description: "The post's ID")
|
arg(:id, non_null(:id), description: "The post's ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Posts.Post,
|
||||||
|
rule: :"write:group:post:delete"
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Post.delete_post/3)
|
resolve(&Post.delete_post/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,11 +11,12 @@ defmodule Mobilizon.GraphQL.Schema.ReportType do
|
||||||
|
|
||||||
@desc "A report object"
|
@desc "A report object"
|
||||||
object :report do
|
object :report do
|
||||||
|
meta(:authorize, :all)
|
||||||
interfaces([:action_log_object])
|
interfaces([:action_log_object])
|
||||||
field(:id, :id, description: "The internal ID of the report")
|
field(:id, :id, description: "The internal ID of the report")
|
||||||
field(:content, :string, description: "The comment the reporter added about this report")
|
field(:content, :string, description: "The comment the reporter added about this report")
|
||||||
field(:status, :report_status, description: "Whether the report is still active")
|
field(:status, :report_status, description: "Whether the report is still active")
|
||||||
field(:uri, :string, description: "The URI of the report")
|
field(:uri, :string, description: "The URI of the report", meta: [private: true])
|
||||||
field(:reported, :actor, description: "The actor that is being reported")
|
field(:reported, :actor, description: "The actor that is being reported")
|
||||||
field(:reporter, :actor, description: "The actor that created the report")
|
field(:reporter, :actor, description: "The actor that created the report")
|
||||||
field(:event, :event, description: "The event that is being reported")
|
field(:event, :event, description: "The event that is being reported")
|
||||||
|
@ -23,6 +24,7 @@ defmodule Mobilizon.GraphQL.Schema.ReportType do
|
||||||
|
|
||||||
field(:notes, list_of(:report_note),
|
field(:notes, list_of(:report_note),
|
||||||
description: "The notes made on the event",
|
description: "The notes made on the event",
|
||||||
|
meta: [private: true],
|
||||||
resolve: dataloader(Reports)
|
resolve: dataloader(Reports)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,12 +33,14 @@ defmodule Mobilizon.GraphQL.Schema.ReportType do
|
||||||
end
|
end
|
||||||
|
|
||||||
object :paginated_report_list do
|
object :paginated_report_list do
|
||||||
|
meta(:authorize, :moderator)
|
||||||
field(:elements, list_of(:report), description: "A list of reports")
|
field(:elements, list_of(:report), description: "A list of reports")
|
||||||
field(:total, :integer, description: "The total number of reports in the list")
|
field(:total, :integer, description: "The total number of reports in the list")
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "A report note object"
|
@desc "A report note object"
|
||||||
object :report_note do
|
object :report_note do
|
||||||
|
meta(:authorize, :moderator)
|
||||||
interfaces([:action_log_object])
|
interfaces([:action_log_object])
|
||||||
field(:id, :id, description: "The internal ID of the report note")
|
field(:id, :id, description: "The internal ID of the report note")
|
||||||
field(:content, :string, description: "The content of the note")
|
field(:content, :string, description: "The content of the note")
|
||||||
|
@ -73,12 +77,20 @@ defmodule Mobilizon.GraphQL.Schema.ReportType do
|
||||||
arg(:limit, :integer, default_value: 10, description: "The limit of reports per page")
|
arg(:limit, :integer, default_value: 10, description: "The limit of reports per page")
|
||||||
arg(:status, :report_status, default_value: :open, description: "Filter reports by status")
|
arg(:status, :report_status, default_value: :open, description: "Filter reports by status")
|
||||||
arg(:domain, :string, default_value: nil, description: "Filter reports by domain name")
|
arg(:domain, :string, default_value: nil, description: "Filter reports by domain name")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :moderator,
|
||||||
|
scope: Mobilizon.Reports.Report,
|
||||||
|
args: %{}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Report.list_reports/3)
|
resolve(&Report.list_reports/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Get a report by id"
|
@desc "Get a report by id"
|
||||||
field :report, :report do
|
field :report, :report do
|
||||||
arg(:id, non_null(:id), description: "The report ID")
|
arg(:id, non_null(:id), description: "The report ID")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :moderator, scope: Mobilizon.Reports.Report)
|
||||||
resolve(&Report.get_report/3)
|
resolve(&Report.get_report/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -101,6 +113,8 @@ defmodule Mobilizon.GraphQL.Schema.ReportType do
|
||||||
"Whether to forward the report to the original instance if the content is remote"
|
"Whether to forward the report to the original instance if the content is remote"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
|
|
||||||
resolve(&Report.create_report/3)
|
resolve(&Report.create_report/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -113,6 +127,12 @@ defmodule Mobilizon.GraphQL.Schema.ReportType do
|
||||||
description: "The feedback to send to the anti-spam system"
|
description: "The feedback to send to the anti-spam system"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :moderator,
|
||||||
|
scope: Mobilizon.Reports.Report,
|
||||||
|
args: %{id: :report_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Report.update_report/3)
|
resolve(&Report.update_report/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -120,12 +140,26 @@ defmodule Mobilizon.GraphQL.Schema.ReportType do
|
||||||
field :create_report_note, type: :report_note do
|
field :create_report_note, type: :report_note do
|
||||||
arg(:content, :string, description: "The note's content")
|
arg(:content, :string, description: "The note's content")
|
||||||
arg(:report_id, non_null(:id), description: "The report's ID")
|
arg(:report_id, non_null(:id), description: "The report's ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :moderator,
|
||||||
|
scope: Mobilizon.Reports.Report,
|
||||||
|
args: %{id: :report_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Report.create_report_note/3)
|
resolve(&Report.create_report_note/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Delete a note on a report"
|
@desc "Delete a note on a report"
|
||||||
field :delete_report_note, type: :deleted_object do
|
field :delete_report_note, type: :deleted_object do
|
||||||
arg(:note_id, non_null(:id), description: "The note's ID")
|
arg(:note_id, non_null(:id), description: "The note's ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :moderator,
|
||||||
|
scope: Mobilizon.Reports.Note,
|
||||||
|
args: %{id: :note_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Report.delete_report_note/3)
|
resolve(&Report.delete_report_note/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Mobilizon.GraphQL.Schema.ResourceType do
|
||||||
|
|
||||||
@desc "A resource"
|
@desc "A resource"
|
||||||
object :resource do
|
object :resource do
|
||||||
|
meta(:authorize, :user)
|
||||||
interfaces([:activity_object])
|
interfaces([:activity_object])
|
||||||
field(:id, :id, description: "The resource's ID")
|
field(:id, :id, description: "The resource's ID")
|
||||||
field(:title, :string, description: "The resource's title")
|
field(:title, :string, description: "The resource's title")
|
||||||
|
@ -44,6 +45,7 @@ defmodule Mobilizon.GraphQL.Schema.ResourceType do
|
||||||
A paginated list of resources
|
A paginated list of resources
|
||||||
"""
|
"""
|
||||||
object :paginated_resource_list do
|
object :paginated_resource_list do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:elements, list_of(:resource), description: "A list of resources")
|
field(:elements, list_of(:resource), description: "A list of resources")
|
||||||
field(:total, :integer, description: "The total number of resources in the list")
|
field(:total, :integer, description: "The total number of resources in the list")
|
||||||
end
|
end
|
||||||
|
@ -52,6 +54,7 @@ defmodule Mobilizon.GraphQL.Schema.ResourceType do
|
||||||
The metadata associated to the resource
|
The metadata associated to the resource
|
||||||
"""
|
"""
|
||||||
object :resource_metadata do
|
object :resource_metadata do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:type, :string, description: "The type of the resource")
|
field(:type, :string, description: "The type of the resource")
|
||||||
field(:title, :string, description: "The resource's metadata title")
|
field(:title, :string, description: "The resource's metadata title")
|
||||||
field(:description, :string, description: "The resource's metadata description")
|
field(:description, :string, description: "The resource's metadata description")
|
||||||
|
@ -84,6 +87,13 @@ defmodule Mobilizon.GraphQL.Schema.ResourceType do
|
||||||
description: "The federated username for the group resource"
|
description: "The federated username for the group resource"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Resources.Resource,
|
||||||
|
rule: :"read:group:resources",
|
||||||
|
args: %{path: :path}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Resource.get_resource/3)
|
resolve(&Resource.get_resource/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -101,6 +111,13 @@ defmodule Mobilizon.GraphQL.Schema.ResourceType do
|
||||||
arg(:resource_url, :string, description: "This resource's own original URL")
|
arg(:resource_url, :string, description: "This resource's own original URL")
|
||||||
arg(:type, :string, default_value: "link", description: "The type for this resource")
|
arg(:type, :string, default_value: "link", description: "The type for this resource")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Resources.Resource,
|
||||||
|
rule: :"write:group:resources:create",
|
||||||
|
args: %{actor_id: :actor_id}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Resource.create_resource/3)
|
resolve(&Resource.create_resource/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -112,18 +129,39 @@ defmodule Mobilizon.GraphQL.Schema.ResourceType do
|
||||||
arg(:parent_id, :id, description: "The new resource parent ID (if the resource is moved)")
|
arg(:parent_id, :id, description: "The new resource parent ID (if the resource is moved)")
|
||||||
arg(:resource_url, :string, description: "The new resource URL")
|
arg(:resource_url, :string, description: "The new resource URL")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Resources.Resource,
|
||||||
|
rule: :"write:group:resources:update"
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Resource.update_resource/3)
|
resolve(&Resource.update_resource/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Delete a resource"
|
@desc "Delete a resource"
|
||||||
field :delete_resource, :deleted_object do
|
field :delete_resource, :deleted_object do
|
||||||
arg(:id, non_null(:id), description: "The resource ID")
|
arg(:id, non_null(:id), description: "The resource ID")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Resources.Resource,
|
||||||
|
rule: :"write:group:resources:delete"
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Resource.delete_resource/3)
|
resolve(&Resource.delete_resource/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Get a preview for a resource link"
|
@desc "Get a preview for a resource link"
|
||||||
field :preview_resource_link, :resource_metadata do
|
field :preview_resource_link, :resource_metadata do
|
||||||
arg(:resource_url, non_null(:string), description: "The link to crawl to get of preview of")
|
arg(:resource_url, non_null(:string), description: "The link to crawl to get of preview of")
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: Mobilizon.Resources.Resource,
|
||||||
|
rule: :"read:group:resources",
|
||||||
|
args: %{}
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&Resource.preview_resource_link/3)
|
resolve(&Resource.preview_resource_link/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,6 +10,7 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
|
||||||
alias Mobilizon.Service.GlobalSearch.{EventResult, GroupResult}
|
alias Mobilizon.Service.GlobalSearch.{EventResult, GroupResult}
|
||||||
|
|
||||||
interface :event_search_result do
|
interface :event_search_result do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:id, :id, description: "Internal ID for this event")
|
field(:id, :id, description: "Internal ID for this event")
|
||||||
field(:uuid, :uuid, description: "The Event UUID")
|
field(:uuid, :uuid, description: "The Event UUID")
|
||||||
field(:url, :string, description: "The ActivityPub Event URL")
|
field(:url, :string, description: "The ActivityPub Event URL")
|
||||||
|
@ -43,6 +44,7 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
|
||||||
|
|
||||||
@desc "Search event result"
|
@desc "Search event result"
|
||||||
object :event_result do
|
object :event_result do
|
||||||
|
meta(:authorize, :all)
|
||||||
interfaces([:event_search_result])
|
interfaces([:event_search_result])
|
||||||
field(:id, :id, description: "Internal ID for this event")
|
field(:id, :id, description: "Internal ID for this event")
|
||||||
field(:uuid, :uuid, description: "The Event UUID")
|
field(:uuid, :uuid, description: "The Event UUID")
|
||||||
|
@ -65,6 +67,7 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
|
||||||
end
|
end
|
||||||
|
|
||||||
interface :group_search_result do
|
interface :group_search_result do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:id, :id, description: "Internal ID for this group")
|
field(:id, :id, description: "Internal ID for this group")
|
||||||
field(:url, :string, description: "The ActivityPub actor's URL")
|
field(:url, :string, description: "The ActivityPub actor's URL")
|
||||||
field(:type, :actor_type, description: "The type of Actor (Person, Group,…)")
|
field(:type, :actor_type, description: "The type of Actor (Person, Group,…)")
|
||||||
|
@ -92,6 +95,7 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
|
||||||
|
|
||||||
@desc "Search group result"
|
@desc "Search group result"
|
||||||
object :group_result do
|
object :group_result do
|
||||||
|
meta(:authorize, :all)
|
||||||
interfaces([:group_search_result])
|
interfaces([:group_search_result])
|
||||||
field(:id, :id, description: "Internal ID for this group")
|
field(:id, :id, description: "Internal ID for this group")
|
||||||
field(:url, :string, description: "The ActivityPub actor's URL")
|
field(:url, :string, description: "The ActivityPub actor's URL")
|
||||||
|
@ -109,18 +113,21 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
|
||||||
|
|
||||||
@desc "Search persons result"
|
@desc "Search persons result"
|
||||||
object :persons do
|
object :persons do
|
||||||
|
meta(:authorize, [:administrator, :moderator])
|
||||||
field(:total, non_null(:integer), description: "Total elements")
|
field(:total, non_null(:integer), description: "Total elements")
|
||||||
field(:elements, non_null(list_of(:person)), description: "Person elements")
|
field(:elements, non_null(list_of(:person)), description: "Person elements")
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Search groups result"
|
@desc "Search groups result"
|
||||||
object :groups do
|
object :groups do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:total, non_null(:integer), description: "Total elements")
|
field(:total, non_null(:integer), description: "Total elements")
|
||||||
field(:elements, non_null(list_of(:group_search_result)), description: "Group elements")
|
field(:elements, non_null(list_of(:group_search_result)), description: "Group elements")
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Search events result"
|
@desc "Search events result"
|
||||||
object :events do
|
object :events do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:total, non_null(:integer), description: "Total elements")
|
field(:total, non_null(:integer), description: "Total elements")
|
||||||
field(:elements, non_null(list_of(:event_search_result)), description: "Event elements")
|
field(:elements, non_null(list_of(:event_search_result)), description: "Event elements")
|
||||||
end
|
end
|
||||||
|
@ -179,7 +186,7 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
|
||||||
arg(:term, :string, default_value: "", description: "Search term")
|
arg(:term, :string, default_value: "", description: "Search term")
|
||||||
arg(:page, :integer, default_value: 1, description: "Result page")
|
arg(:page, :integer, default_value: 1, description: "Result page")
|
||||||
arg(:limit, :integer, default_value: 10, description: "Results limit per page")
|
arg(:limit, :integer, default_value: 10, description: "Results limit per page")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: [:administrator, :moderator], scope: false)
|
||||||
resolve(&Search.search_persons/3)
|
resolve(&Search.search_persons/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -225,6 +232,7 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
|
||||||
description: "How to sort search results"
|
description: "How to sort search results"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&Search.search_groups/3)
|
resolve(&Search.search_groups/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -275,13 +283,14 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do
|
||||||
description: "How to sort search results"
|
description: "How to sort search results"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&Search.search_events/3)
|
resolve(&Search.search_events/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Interact with an URI"
|
@desc "Interact with an URI"
|
||||||
field :interact, :interactable do
|
field :interact, :interactable do
|
||||||
arg(:uri, non_null(:string), description: "The URI for to interact with")
|
arg(:uri, non_null(:string), description: "The URI for to interact with")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&Search.interact/3)
|
resolve(&Search.interact/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Mobilizon.GraphQL.Schema.StatisticsType do
|
||||||
|
|
||||||
@desc "A statistics object"
|
@desc "A statistics object"
|
||||||
object :statistics do
|
object :statistics do
|
||||||
|
meta(:authorize, :all)
|
||||||
# Instance name
|
# Instance name
|
||||||
field(:number_of_users, :integer, description: "The number of local users")
|
field(:number_of_users, :integer, description: "The number of local users")
|
||||||
field(:number_of_events, :integer, description: "The total number of events")
|
field(:number_of_events, :integer, description: "The total number of events")
|
||||||
|
@ -27,6 +28,7 @@ defmodule Mobilizon.GraphQL.Schema.StatisticsType do
|
||||||
end
|
end
|
||||||
|
|
||||||
object :category_statistics do
|
object :category_statistics do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:key, :string, description: "The key for the category")
|
field(:key, :string, description: "The key for the category")
|
||||||
field(:number, :integer, description: "The number of events for the given category")
|
field(:number, :integer, description: "The number of events for the given category")
|
||||||
end
|
end
|
||||||
|
@ -34,11 +36,13 @@ defmodule Mobilizon.GraphQL.Schema.StatisticsType do
|
||||||
object :statistics_queries do
|
object :statistics_queries do
|
||||||
@desc "Get the instance statistics"
|
@desc "Get the instance statistics"
|
||||||
field :statistics, :statistics do
|
field :statistics, :statistics do
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&Statistics.get_statistics/3)
|
resolve(&Statistics.get_statistics/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Get the instance's category statistics"
|
@desc "Get the instance's category statistics"
|
||||||
field :category_statistics, list_of(:category_statistics) do
|
field :category_statistics, list_of(:category_statistics) do
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&Statistics.get_category_statistics/3)
|
resolve(&Statistics.get_category_statistics/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Mobilizon.GraphQL.Schema.TagType do
|
||||||
|
|
||||||
@desc "A tag"
|
@desc "A tag"
|
||||||
object :tag do
|
object :tag do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:id, :id, description: "The tag's ID")
|
field(:id, :id, description: "The tag's ID")
|
||||||
field(:slug, :string, description: "The tags's slug")
|
field(:slug, :string, description: "The tags's slug")
|
||||||
field(:title, :string, description: "The tag's title")
|
field(:title, :string, description: "The tag's title")
|
||||||
|
@ -26,6 +27,7 @@ defmodule Mobilizon.GraphQL.Schema.TagType do
|
||||||
arg(:filter, :string, description: "The filter to apply to the search")
|
arg(:filter, :string, description: "The filter to apply to the search")
|
||||||
arg(:page, :integer, default_value: 1, description: "The page in the paginated tags list")
|
arg(:page, :integer, default_value: 1, description: "The page in the paginated tags list")
|
||||||
arg(:limit, :integer, default_value: 10, description: "The limit of tags per page")
|
arg(:limit, :integer, default_value: 10, description: "The limit of tags per page")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&Tag.list_tags/3)
|
resolve(&Tag.list_tags/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoType do
|
||||||
|
|
||||||
@desc "A todo"
|
@desc "A todo"
|
||||||
object :todo do
|
object :todo do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:id, :id, description: "The todo's ID")
|
field(:id, :id, description: "The todo's ID")
|
||||||
field(:title, :string, description: "The todo's title")
|
field(:title, :string, description: "The todo's title")
|
||||||
field(:status, :boolean, description: "The todo's status")
|
field(:status, :boolean, description: "The todo's status")
|
||||||
|
@ -30,6 +31,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoType do
|
||||||
A paginated list of todos
|
A paginated list of todos
|
||||||
"""
|
"""
|
||||||
object :paginated_todo_list do
|
object :paginated_todo_list do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:elements, list_of(:todo), description: "A list of todos")
|
field(:elements, list_of(:todo), description: "A list of todos")
|
||||||
field(:total, :integer, description: "The total number of todos in the list")
|
field(:total, :integer, description: "The total number of todos in the list")
|
||||||
end
|
end
|
||||||
|
@ -38,6 +40,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoType do
|
||||||
@desc "Get a todo"
|
@desc "Get a todo"
|
||||||
field :todo, :todo do
|
field :todo, :todo do
|
||||||
arg(:id, non_null(:id), description: "The todo ID")
|
arg(:id, non_null(:id), description: "The todo ID")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&TodoResolver.get_todo/3)
|
resolve(&TodoResolver.get_todo/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -50,6 +53,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoType do
|
||||||
arg(:status, :boolean, description: "The todo status")
|
arg(:status, :boolean, description: "The todo status")
|
||||||
arg(:due_date, :datetime, description: "The todo due date")
|
arg(:due_date, :datetime, description: "The todo due date")
|
||||||
arg(:assigned_to_id, :id, description: "The actor this todo is assigned to")
|
arg(:assigned_to_id, :id, description: "The actor this todo is assigned to")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
|
|
||||||
resolve(&TodoResolver.create_todo/3)
|
resolve(&TodoResolver.create_todo/3)
|
||||||
end
|
end
|
||||||
|
@ -62,7 +66,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoType do
|
||||||
arg(:status, :boolean, description: "The new todo status")
|
arg(:status, :boolean, description: "The new todo status")
|
||||||
arg(:due_date, :datetime, description: "The new todo due date")
|
arg(:due_date, :datetime, description: "The new todo due date")
|
||||||
arg(:assigned_to_id, :id, description: "The new id of the actor this todo is assigned to")
|
arg(:assigned_to_id, :id, description: "The new id of the actor this todo is assigned to")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&TodoResolver.update_todo/3)
|
resolve(&TodoResolver.update_todo/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoListType do
|
||||||
|
|
||||||
@desc "A todo list"
|
@desc "A todo list"
|
||||||
object :todo_list do
|
object :todo_list do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:id, :id, description: "The todo list's ID")
|
field(:id, :id, description: "The todo list's ID")
|
||||||
field(:title, :string, description: "The todo list's title")
|
field(:title, :string, description: "The todo list's title")
|
||||||
|
|
||||||
|
@ -37,6 +38,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoListType do
|
||||||
A paginated list of todo-lists
|
A paginated list of todo-lists
|
||||||
"""
|
"""
|
||||||
object :paginated_todo_list_list do
|
object :paginated_todo_list_list do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:elements, list_of(:todo_list), description: "A list of todo lists")
|
field(:elements, list_of(:todo_list), description: "A list of todo lists")
|
||||||
field(:total, :integer, description: "The total number of todo lists in the list")
|
field(:total, :integer, description: "The total number of todo lists in the list")
|
||||||
end
|
end
|
||||||
|
@ -45,6 +47,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoListType do
|
||||||
@desc "Get a todo list"
|
@desc "Get a todo list"
|
||||||
field :todo_list, :todo_list do
|
field :todo_list, :todo_list do
|
||||||
arg(:id, non_null(:id), description: "The todo-list ID")
|
arg(:id, non_null(:id), description: "The todo-list ID")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&Todos.get_todo_list/3)
|
resolve(&Todos.get_todo_list/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -54,6 +57,7 @@ defmodule Mobilizon.GraphQL.Schema.Todos.TodoListType do
|
||||||
field :create_todo_list, :todo_list do
|
field :create_todo_list, :todo_list do
|
||||||
arg(:title, non_null(:string), description: "The todo list title")
|
arg(:title, non_null(:string), description: "The todo list title")
|
||||||
arg(:group_id, non_null(:id), description: "The group ID")
|
arg(:group_id, non_null(:id), description: "The group ID")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&Todos.create_todo_list/3)
|
resolve(&Todos.create_todo_list/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,6 +15,8 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
|
|
||||||
@desc "A local user of Mobilizon"
|
@desc "A local user of Mobilizon"
|
||||||
object :user do
|
object :user do
|
||||||
|
meta(:authorize, :all)
|
||||||
|
meta(:scope_field?, true)
|
||||||
interfaces([:action_log_object])
|
interfaces([:action_log_object])
|
||||||
field(:id, :id, description: "The user's ID")
|
field(:id, :id, description: "The user's ID")
|
||||||
field(:email, non_null(:string), description: "The user's email")
|
field(:email, non_null(:string), description: "The user's email")
|
||||||
|
@ -63,7 +65,8 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
field(:disabled, :boolean, description: "Whether the user is disabled")
|
field(:disabled, :boolean, description: "Whether the user is disabled")
|
||||||
|
|
||||||
field(:participations, :paginated_participant_list,
|
field(:participations, :paginated_participant_list,
|
||||||
description: "The list of participations this user has"
|
description: "The list of participations this user has",
|
||||||
|
meta: [private: true]
|
||||||
) do
|
) do
|
||||||
arg(:after_datetime, :datetime, description: "Filter participations by event start datetime")
|
arg(:after_datetime, :datetime, description: "Filter participations by event start datetime")
|
||||||
|
|
||||||
|
@ -83,7 +86,8 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
end
|
end
|
||||||
|
|
||||||
field(:memberships, :paginated_member_list,
|
field(:memberships, :paginated_member_list,
|
||||||
description: "The list of memberships for this user"
|
description: "The list of memberships for this user",
|
||||||
|
meta: [private: true]
|
||||||
) do
|
) do
|
||||||
arg(:name, :string, description: "A name to filter members by")
|
arg(:name, :string, description: "A name to filter members by")
|
||||||
|
|
||||||
|
@ -97,7 +101,8 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
end
|
end
|
||||||
|
|
||||||
field(:drafts, :paginated_event_list,
|
field(:drafts, :paginated_event_list,
|
||||||
description: "The list of draft events this user has created"
|
description: "The list of draft events this user has created",
|
||||||
|
meta: [private: true]
|
||||||
) do
|
) do
|
||||||
arg(:page, :integer,
|
arg(:page, :integer,
|
||||||
default_value: 1,
|
default_value: 1,
|
||||||
|
@ -109,7 +114,8 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
end
|
end
|
||||||
|
|
||||||
field(:followed_group_events, :paginated_followed_group_events,
|
field(:followed_group_events, :paginated_followed_group_events,
|
||||||
description: "The suggested events from the groups this user follows"
|
description: "The suggested events from the groups this user follows",
|
||||||
|
meta: [private: true]
|
||||||
) do
|
) do
|
||||||
arg(:page, :integer,
|
arg(:page, :integer,
|
||||||
default_value: 1,
|
default_value: 1,
|
||||||
|
@ -128,7 +134,10 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
resolve(&User.user_followed_group_events/3)
|
resolve(&User.user_followed_group_events/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
field(:settings, :user_settings, description: "The list of settings for this user") do
|
field(:settings, :user_settings,
|
||||||
|
description: "The list of settings for this user",
|
||||||
|
meta: [private: true]
|
||||||
|
) do
|
||||||
resolve(&User.user_settings/3)
|
resolve(&User.user_settings/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -142,7 +151,10 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
description: "The IP adress the user's currently signed-in with"
|
description: "The IP adress the user's currently signed-in with"
|
||||||
)
|
)
|
||||||
|
|
||||||
field(:media, :paginated_media_list, description: "The user's media objects") do
|
field(:media, :paginated_media_list,
|
||||||
|
description: "The user's media objects",
|
||||||
|
meta: [private: true]
|
||||||
|
) do
|
||||||
arg(:page, :integer,
|
arg(:page, :integer,
|
||||||
default_value: 1,
|
default_value: 1,
|
||||||
description: "The page in the paginated user media list"
|
description: "The page in the paginated user media list"
|
||||||
|
@ -158,14 +170,18 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
)
|
)
|
||||||
|
|
||||||
field(:activity_settings, list_of(:activity_setting),
|
field(:activity_settings, list_of(:activity_setting),
|
||||||
resolve: &ActivitySettings.user_activity_settings/3,
|
description: "The user's activity settings",
|
||||||
description: "The user's activity settings"
|
meta: [private: true]
|
||||||
)
|
) do
|
||||||
|
resolve(&ActivitySettings.user_activity_settings/3)
|
||||||
|
end
|
||||||
|
|
||||||
field(:auth_authorized_applications, list_of(:auth_application_token),
|
field(:auth_authorized_applications, list_of(:auth_application_token),
|
||||||
resolve: &Application.get_user_applications/3,
|
description: "The user's authorized authentication apps",
|
||||||
description: "The user's authorized authentication apps"
|
meta: [private: true, rule: :forbid_app_access]
|
||||||
)
|
) do
|
||||||
|
resolve(&Application.get_user_applications/3)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "The list of roles an user can have"
|
@desc "The list of roles an user can have"
|
||||||
|
@ -177,12 +193,14 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
|
|
||||||
@desc "Token"
|
@desc "Token"
|
||||||
object :refreshed_token do
|
object :refreshed_token do
|
||||||
|
meta(:authorize, :all)
|
||||||
field(:access_token, non_null(:string), description: "Generated access token")
|
field(:access_token, non_null(:string), description: "Generated access token")
|
||||||
field(:refresh_token, non_null(:string), description: "Generated refreshed token")
|
field(:refresh_token, non_null(:string), description: "Generated refreshed token")
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Users list"
|
@desc "Users list"
|
||||||
object :users do
|
object :users do
|
||||||
|
meta(:authorize, [:administrator, :moderator])
|
||||||
field(:total, non_null(:integer), description: "Total elements")
|
field(:total, non_null(:integer), description: "Total elements")
|
||||||
field(:elements, non_null(list_of(:user)), description: "User elements")
|
field(:elements, non_null(list_of(:user)), description: "User elements")
|
||||||
end
|
end
|
||||||
|
@ -196,6 +214,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
A set of user settings
|
A set of user settings
|
||||||
"""
|
"""
|
||||||
object :user_settings do
|
object :user_settings do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:timezone, :string, description: "The timezone for this user")
|
field(:timezone, :string, description: "The timezone for this user")
|
||||||
|
|
||||||
field(:notification_on_day, :boolean,
|
field(:notification_on_day, :boolean,
|
||||||
|
@ -254,6 +273,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
end
|
end
|
||||||
|
|
||||||
object :location do
|
object :location do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:range, :integer, description: "The range in kilometers the user wants to see events")
|
field(:range, :integer, description: "The range in kilometers the user wants to see events")
|
||||||
|
|
||||||
field(:geohash, :string, description: "A geohash representing the user's preferred location")
|
field(:geohash, :string, description: "A geohash representing the user's preferred location")
|
||||||
|
@ -276,11 +296,13 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
@desc "Get an user"
|
@desc "Get an user"
|
||||||
field :user, :user do
|
field :user, :user do
|
||||||
arg(:id, non_null(:id))
|
arg(:id, non_null(:id))
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: [:administrator, :moderator], scope: false)
|
||||||
resolve(&User.find_user/3)
|
resolve(&User.find_user/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Get the current user"
|
@desc "Get the current user"
|
||||||
field :logged_user, :user do
|
field :logged_user, :user do
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&User.get_current_user/3)
|
resolve(&User.get_current_user/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -297,7 +319,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
|
|
||||||
arg(:sort, :sortable_user_field, default_value: :id, description: "Sort column")
|
arg(:sort, :sortable_user_field, default_value: :id, description: "Sort column")
|
||||||
arg(:direction, :sort_direction, default_value: :desc, description: "Sort direction")
|
arg(:direction, :sort_direction, default_value: :desc, description: "Sort direction")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: [:administrator, :moderator], scope: false)
|
||||||
resolve(&User.list_users/3)
|
resolve(&User.list_users/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -308,7 +330,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
arg(:email, non_null(:string), description: "The new user's email")
|
arg(:email, non_null(:string), description: "The new user's email")
|
||||||
arg(:password, non_null(:string), description: "The new user's password")
|
arg(:password, non_null(:string), description: "The new user's password")
|
||||||
arg(:locale, :string, description: "The new user's locale")
|
arg(:locale, :string, description: "The new user's locale")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&User.create_user/3)
|
resolve(&User.create_user/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -318,6 +340,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
description: "The token that will be used to validate the user"
|
description: "The token that will be used to validate the user"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&User.validate_user/3)
|
resolve(&User.validate_user/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -325,6 +348,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
field :resend_confirmation_email, type: :string do
|
field :resend_confirmation_email, type: :string do
|
||||||
arg(:email, non_null(:string), description: "The email used to register")
|
arg(:email, non_null(:string), description: "The email used to register")
|
||||||
arg(:locale, :string, description: "The user's locale")
|
arg(:locale, :string, description: "The user's locale")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&User.resend_confirmation_email/3)
|
resolve(&User.resend_confirmation_email/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -332,6 +356,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
field :send_reset_password, type: :string do
|
field :send_reset_password, type: :string do
|
||||||
arg(:email, non_null(:string), description: "The user's email")
|
arg(:email, non_null(:string), description: "The user's email")
|
||||||
arg(:locale, :string, description: "The user's locale")
|
arg(:locale, :string, description: "The user's locale")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&User.send_reset_password/3)
|
resolve(&User.send_reset_password/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -343,6 +368,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
|
|
||||||
arg(:password, non_null(:string), description: "The new password")
|
arg(:password, non_null(:string), description: "The new password")
|
||||||
arg(:locale, :string, default_value: "en", description: "The user's locale")
|
arg(:locale, :string, default_value: "en", description: "The user's locale")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&User.reset_password/3)
|
resolve(&User.reset_password/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -350,24 +376,28 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
field :login, type: :login do
|
field :login, type: :login do
|
||||||
arg(:email, non_null(:string), description: "The user's email")
|
arg(:email, non_null(:string), description: "The user's email")
|
||||||
arg(:password, non_null(:string), description: "The user's password")
|
arg(:password, non_null(:string), description: "The user's password")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&User.login_user/3)
|
resolve(&User.login_user/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Refresh a token"
|
@desc "Refresh a token"
|
||||||
field :refresh_token, type: :refreshed_token do
|
field :refresh_token, type: :refreshed_token do
|
||||||
arg(:refresh_token, non_null(:string), description: "A refresh token")
|
arg(:refresh_token, non_null(:string), description: "A refresh token")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&User.refresh_token/3)
|
resolve(&User.refresh_token/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Logout an user, deleting a refresh token"
|
@desc "Logout an user, deleting a refresh token"
|
||||||
field :logout, :string do
|
field :logout, :string do
|
||||||
arg(:refresh_token, non_null(:string))
|
arg(:refresh_token, non_null(:string))
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&User.logout/3)
|
resolve(&User.logout/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Change default actor for user"
|
@desc "Change default actor for user"
|
||||||
field :change_default_actor, :user do
|
field :change_default_actor, :user do
|
||||||
arg(:preferred_username, non_null(:string), description: "The actor preferred_username")
|
arg(:preferred_username, non_null(:string), description: "The actor preferred_username")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&User.change_default_actor/3)
|
resolve(&User.change_default_actor/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -375,6 +405,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
field :change_password, :user do
|
field :change_password, :user do
|
||||||
arg(:old_password, non_null(:string), description: "The user's current password")
|
arg(:old_password, non_null(:string), description: "The user's current password")
|
||||||
arg(:new_password, non_null(:string), description: "The user's new password")
|
arg(:new_password, non_null(:string), description: "The user's new password")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&User.change_password/3)
|
resolve(&User.change_password/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -382,6 +413,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
field :change_email, :user do
|
field :change_email, :user do
|
||||||
arg(:email, non_null(:string), description: "The user's new email")
|
arg(:email, non_null(:string), description: "The user's new email")
|
||||||
arg(:password, non_null(:string), description: "The user's current password")
|
arg(:password, non_null(:string), description: "The user's current password")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&User.change_email/3)
|
resolve(&User.change_email/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -391,6 +423,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
description: "The token that will be used to validate the email change"
|
description: "The token that will be used to validate the email change"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
resolve(&User.validate_email/3)
|
resolve(&User.validate_email/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -398,6 +431,7 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
field :delete_account, :deleted_object do
|
field :delete_account, :deleted_object do
|
||||||
arg(:password, :string, description: "The user's password")
|
arg(:password, :string, description: "The user's password")
|
||||||
arg(:user_id, :id, description: "The user's ID")
|
arg(:user_id, :id, description: "The user's ID")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&User.delete_account/3)
|
resolve(&User.delete_account/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -435,12 +469,14 @@ defmodule Mobilizon.GraphQL.Schema.UserType do
|
||||||
description: "A geohash of the user's preferred location, where they want to see events"
|
description: "A geohash of the user's preferred location, where they want to see events"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&User.set_user_setting/3)
|
resolve(&User.set_user_setting/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
@desc "Update the user's locale"
|
@desc "Update the user's locale"
|
||||||
field :update_locale, :user do
|
field :update_locale, :user do
|
||||||
arg(:locale, :string, description: "The user's new locale")
|
arg(:locale, :string, description: "The user's new locale")
|
||||||
|
middleware(Rajska.QueryAuthorization, permit: :user, scope: false)
|
||||||
resolve(&User.update_locale/3)
|
resolve(&User.update_locale/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Mobilizon.GraphQL.Schema.Users.ActivitySetting do
|
||||||
alias Mobilizon.GraphQL.Resolvers.Users.ActivitySettings
|
alias Mobilizon.GraphQL.Resolvers.Users.ActivitySettings
|
||||||
|
|
||||||
object :activity_setting do
|
object :activity_setting do
|
||||||
|
meta(:authorize, :user)
|
||||||
field(:key, :string)
|
field(:key, :string)
|
||||||
field(:method, :string)
|
field(:method, :string)
|
||||||
field(:enabled, :boolean)
|
field(:enabled, :boolean)
|
||||||
|
@ -17,6 +18,13 @@ defmodule Mobilizon.GraphQL.Schema.Users.ActivitySetting do
|
||||||
arg(:key, non_null(:string))
|
arg(:key, non_null(:string))
|
||||||
arg(:method, non_null(:string))
|
arg(:method, non_null(:string))
|
||||||
arg(:enabled, non_null(:boolean))
|
arg(:enabled, non_null(:boolean))
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: false,
|
||||||
|
rule: :"write:user:setting:activity"
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&ActivitySettings.upsert_user_activity_setting/3)
|
resolve(&ActivitySettings.upsert_user_activity_setting/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,11 +26,25 @@ defmodule Mobilizon.GraphQL.Schema.Users.PushSubscription do
|
||||||
arg(:endpoint, non_null(:string))
|
arg(:endpoint, non_null(:string))
|
||||||
arg(:auth, non_null(:string))
|
arg(:auth, non_null(:string))
|
||||||
arg(:p256dh, non_null(:string))
|
arg(:p256dh, non_null(:string))
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: false,
|
||||||
|
rule: :"write:user:setting:push"
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&PushSubscription.register_push_subscription/3)
|
resolve(&PushSubscription.register_push_subscription/3)
|
||||||
end
|
end
|
||||||
|
|
||||||
field :unregister_push, :string do
|
field :unregister_push, :string do
|
||||||
arg(:endpoint, non_null(:string))
|
arg(:endpoint, non_null(:string))
|
||||||
|
|
||||||
|
middleware(Rajska.QueryAuthorization,
|
||||||
|
permit: :user,
|
||||||
|
scope: false,
|
||||||
|
rule: :"write:user:setting:push"
|
||||||
|
)
|
||||||
|
|
||||||
resolve(&PushSubscription.unregister_push_subscription/3)
|
resolve(&PushSubscription.unregister_push_subscription/3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,6 +12,7 @@ defmodule Mobilizon.Applications do
|
||||||
defenum(ApplicationDeviceActivationStatus, [
|
defenum(ApplicationDeviceActivationStatus, [
|
||||||
"success",
|
"success",
|
||||||
"pending",
|
"pending",
|
||||||
|
"confirmed",
|
||||||
"incorrect_device_code",
|
"incorrect_device_code",
|
||||||
"access_denied"
|
"access_denied"
|
||||||
])
|
])
|
||||||
|
@ -144,12 +145,15 @@ defmodule Mobilizon.Applications do
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def list_application_tokens do
|
def list_application_tokens do
|
||||||
Repo.all(ApplicationToken)
|
ApplicationToken
|
||||||
|
|> Repo.all()
|
||||||
|
|> Repo.preload(:application)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns the list of application tokens for a given user_id
|
Returns the list of application tokens for a given user_id
|
||||||
"""
|
"""
|
||||||
|
@spec list_application_tokens_for_user_id(number() | String.t()) :: list(ApplicationToken.t())
|
||||||
def list_application_tokens_for_user_id(user_id) do
|
def list_application_tokens_for_user_id(user_id) do
|
||||||
ApplicationToken
|
ApplicationToken
|
||||||
|> where(user_id: ^user_id)
|
|> where(user_id: ^user_id)
|
||||||
|
@ -172,7 +176,11 @@ defmodule Mobilizon.Applications do
|
||||||
** (Ecto.NoResultsError)
|
** (Ecto.NoResultsError)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def get_application_token!(id), do: Repo.get!(ApplicationToken, id)
|
def get_application_token!(id) do
|
||||||
|
ApplicationToken
|
||||||
|
|> Repo.get!(id)
|
||||||
|
|> Repo.preload([:application])
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Gets a single application_token.
|
Gets a single application_token.
|
||||||
|
@ -211,6 +219,13 @@ defmodule Mobilizon.Applications do
|
||||||
%ApplicationToken{}
|
%ApplicationToken{}
|
||||||
|> ApplicationToken.changeset(attrs)
|
|> ApplicationToken.changeset(attrs)
|
||||||
|> Repo.insert(on_conflict: :replace_all, conflict_target: [:user_id, :application_id])
|
|> Repo.insert(on_conflict: :replace_all, conflict_target: [:user_id, :application_id])
|
||||||
|
|> case do
|
||||||
|
{:ok, application_token} ->
|
||||||
|
{:ok, Repo.preload(application_token, :application)}
|
||||||
|
|
||||||
|
error ->
|
||||||
|
error
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -303,10 +318,14 @@ defmodule Mobilizon.Applications do
|
||||||
|
|
||||||
def get_application_device_activation(id), do: Repo.get(ApplicationDeviceActivation, id)
|
def get_application_device_activation(id), do: Repo.get(ApplicationDeviceActivation, id)
|
||||||
|
|
||||||
def get_application_device_activation_by_user_code(user_code),
|
def get_application_device_activation_by_user_code(user_code) do
|
||||||
do: Repo.get_by(ApplicationDeviceActivation, user_code: user_code)
|
ApplicationDeviceActivation
|
||||||
|
|> where([ada], ada.user_code == ^user_code)
|
||||||
|
|> preload(:application)
|
||||||
|
|> Repo.one()
|
||||||
|
end
|
||||||
|
|
||||||
def get_application_device_activation(client_id, device_code) do
|
def get_application_device_activation_by_device_code(client_id, device_code) do
|
||||||
ApplicationDeviceActivation
|
ApplicationDeviceActivation
|
||||||
|> join(:left, [ada], a in assoc(ada, :application))
|
|> join(:left, [ada], a in assoc(ada, :application))
|
||||||
|> where([_, a], a.client_id == ^client_id)
|
|> where([_, a], a.client_id == ^client_id)
|
||||||
|
|
|
@ -6,16 +6,16 @@ defmodule Mobilizon.Applications.Application do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
@required_attrs [:name, :client_id, :client_secret, :redirect_uris]
|
@required_attrs [:name, :client_id, :client_secret, :redirect_uris, :scope]
|
||||||
@optional_attrs [:scopes, :website, :owner_type, :owner_id]
|
@optional_attrs [:website, :owner_type, :owner_id]
|
||||||
@attrs @required_attrs ++ @optional_attrs
|
@attrs @required_attrs ++ @optional_attrs
|
||||||
|
|
||||||
schema "applications" do
|
schema "applications" do
|
||||||
field(:name, :string)
|
field(:name, :string)
|
||||||
field(:client_id, :string)
|
field(:client_id, :string)
|
||||||
field(:client_secret, :string)
|
field(:client_secret, :string)
|
||||||
field(:redirect_uris, :string)
|
field(:redirect_uris, {:array, :string})
|
||||||
field(:scopes, :string)
|
field(:scope, :string)
|
||||||
field(:website, :string)
|
field(:website, :string)
|
||||||
field(:owner_type, :string)
|
field(:owner_type, :string)
|
||||||
field(:owner_id, :integer)
|
field(:owner_id, :integer)
|
||||||
|
|
|
@ -19,7 +19,7 @@ defmodule Mobilizon.Applications.ApplicationDeviceActivation do
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
@required_attrs [:user_code, :device_code, :expires_in, :application_id]
|
@required_attrs [:user_code, :device_code, :expires_in, :application_id, :scope]
|
||||||
@optional_attrs [:status, :user_id]
|
@optional_attrs [:status, :user_id]
|
||||||
@attrs @required_attrs ++ @optional_attrs
|
@attrs @required_attrs ++ @optional_attrs
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ defmodule Mobilizon.Applications.ApplicationToken do
|
||||||
belongs_to(:user, User)
|
belongs_to(:user, User)
|
||||||
belongs_to(:application, Application)
|
belongs_to(:application, Application)
|
||||||
field(:authorization_code, :string)
|
field(:authorization_code, :string)
|
||||||
field(:status, ApplicationTokenStatus)
|
field(:status, ApplicationTokenStatus, default: :pending)
|
||||||
field(:scope, :string)
|
field(:scope, :string)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
|
|
|
@ -4,9 +4,12 @@ defmodule Mobilizon.Service.Auth.Applications do
|
||||||
"""
|
"""
|
||||||
alias Mobilizon.Applications
|
alias Mobilizon.Applications
|
||||||
alias Mobilizon.Applications.{Application, ApplicationDeviceActivation, ApplicationToken}
|
alias Mobilizon.Applications.{Application, ApplicationDeviceActivation, ApplicationToken}
|
||||||
|
alias Mobilizon.GraphQL.Authorization.AppScope
|
||||||
alias Mobilizon.Service.Auth.Authenticator
|
alias Mobilizon.Service.Auth.Authenticator
|
||||||
alias Mobilizon.Users.User
|
alias Mobilizon.Users.User
|
||||||
|
alias Mobilizon.Web.Auth.Guardian
|
||||||
alias Mobilizon.Web.Router.Helpers, as: Routes
|
alias Mobilizon.Web.Router.Helpers, as: Routes
|
||||||
|
require Logger
|
||||||
|
|
||||||
@app_access_tokens_ttl {8, :hour}
|
@app_access_tokens_ttl {8, :hour}
|
||||||
@app_refresh_tokens_ttl {26, :week}
|
@app_refresh_tokens_ttl {26, :week}
|
||||||
|
@ -20,37 +23,43 @@ defmodule Mobilizon.Service.Auth.Applications do
|
||||||
required(:token_type) => String.t()
|
required(:token_type) => String.t()
|
||||||
}
|
}
|
||||||
|
|
||||||
def create(name, redirect_uris, scopes, website) do
|
@spec create(String.t(), list(String.t()), String.t(), String.t() | nil) ::
|
||||||
|
{:ok, Application.t()} | {:error, Ecto.Changeset.t()} | {:error, :invalid_scope}
|
||||||
|
def create(name, redirect_uris, scope, website \\ nil) do
|
||||||
|
if AppScope.scopes_valid?(scope) do
|
||||||
client_id = :crypto.strong_rand_bytes(42) |> Base.encode64() |> binary_part(0, 42)
|
client_id = :crypto.strong_rand_bytes(42) |> Base.encode64() |> binary_part(0, 42)
|
||||||
client_secret = :crypto.strong_rand_bytes(42) |> Base.encode64() |> binary_part(0, 42)
|
client_secret = :crypto.strong_rand_bytes(42) |> Base.encode64() |> binary_part(0, 42)
|
||||||
|
|
||||||
Applications.create_application(%{
|
Applications.create_application(%{
|
||||||
name: name,
|
name: name,
|
||||||
redirect_uris: redirect_uris,
|
redirect_uris: redirect_uris,
|
||||||
scopes: scopes,
|
scope: scope,
|
||||||
website: website,
|
website: website,
|
||||||
client_id: client_id,
|
client_id: client_id,
|
||||||
client_secret: client_secret
|
client_secret: client_secret
|
||||||
})
|
})
|
||||||
|
else
|
||||||
|
{:error, :invalid_scope}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec autorize(String.t(), String.t(), String.t(), integer()) ::
|
@spec autorize(String.t(), String.t(), String.t(), integer()) ::
|
||||||
{:ok, String.t()}
|
{:ok, ApplicationToken.t()}
|
||||||
| {:error, :application_not_found}
|
| {:error, :application_not_found}
|
||||||
| {:error, :redirect_uri_not_in_allowed}
|
| {:error, :redirect_uri_not_in_allowed}
|
||||||
def autorize(client_id, redirect_uri, _scope, user_id) do
|
| {:error, Ecto.Changeset.t()}
|
||||||
|
def autorize(client_id, redirect_uri, scope, user_id) do
|
||||||
with %Application{redirect_uris: redirect_uris, id: app_id} <-
|
with %Application{redirect_uris: redirect_uris, id: app_id} <-
|
||||||
Applications.get_application_by_client_id(client_id),
|
Applications.get_application_by_client_id(client_id),
|
||||||
{:redirect_uri, true} <-
|
{:redirect_uri, true} <-
|
||||||
{:redirect_uri, redirect_uri in String.split(redirect_uris, "\n")},
|
{:redirect_uri, redirect_uri in redirect_uris},
|
||||||
code <- :crypto.strong_rand_bytes(16) |> Base.encode64() |> binary_part(0, 16),
|
code <- :crypto.strong_rand_bytes(16) |> Base.encode64() |> binary_part(0, 16) do
|
||||||
{:ok, %ApplicationToken{}} <-
|
|
||||||
Applications.create_application_token(%{
|
Applications.create_application_token(%{
|
||||||
user_id: user_id,
|
user_id: user_id,
|
||||||
application_id: app_id,
|
application_id: app_id,
|
||||||
authorization_code: code
|
authorization_code: code,
|
||||||
}) do
|
scope: scope
|
||||||
{:ok, code}
|
})
|
||||||
else
|
else
|
||||||
nil ->
|
nil ->
|
||||||
{:error, :application_not_found}
|
{:error, :application_not_found}
|
||||||
|
@ -60,35 +69,62 @@ defmodule Mobilizon.Service.Auth.Applications do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec autorize_device_application(String.t(), String.t()) ::
|
||||||
|
{:ok, ApplicationDeviceActivation.t()}
|
||||||
|
| {:error, Ecto.Changeset.t()}
|
||||||
|
| {:error, :expired}
|
||||||
|
| {:error, :access_denied}
|
||||||
|
| {:error, :not_found}
|
||||||
def autorize_device_application(client_id, user_code) do
|
def autorize_device_application(client_id, user_code) do
|
||||||
case Applications.get_application_device_activation(client_id, user_code) do
|
Logger.debug(
|
||||||
%ApplicationDeviceActivation{status: :confirmed} = app_device_activation ->
|
"Authorizing device application client_id: #{client_id}, user_code: #{user_code}"
|
||||||
|
)
|
||||||
|
|
||||||
|
case Applications.get_application_device_activation_by_user_code(user_code) do
|
||||||
|
%ApplicationDeviceActivation{
|
||||||
|
status: :confirmed,
|
||||||
|
application: %Application{client_id: ^client_id}
|
||||||
|
} = app_device_activation ->
|
||||||
|
if device_activation_expired?(app_device_activation) do
|
||||||
|
{:error, :expired}
|
||||||
|
else
|
||||||
Applications.update_application_device_activation(app_device_activation, %{
|
Applications.update_application_device_activation(app_device_activation, %{
|
||||||
status: :success
|
status: :success
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# The device activation is confirmed, but does not match the given app client_id, so we say it's not found
|
||||||
|
%ApplicationDeviceActivation{status: :confirmed} ->
|
||||||
|
{:error, :not_found}
|
||||||
|
|
||||||
|
%ApplicationDeviceActivation{} ->
|
||||||
|
{:error, :not_confirmed}
|
||||||
|
|
||||||
|
nil ->
|
||||||
|
{:error, :not_found}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec generate_access_token(String.t(), String.t(), String.t(), String.t()) ::
|
@spec generate_access_token(String.t(), String.t(), String.t(), String.t(), String.t()) ::
|
||||||
{:ok, access_token_details()}
|
{:ok, access_token_details()}
|
||||||
| {:error,
|
| {:error,
|
||||||
:application_not_found
|
:application_not_found
|
||||||
| :redirect_uri_not_in_allowed
|
| :redirect_uri_not_in_allowed
|
||||||
| :provided_code_does_not_match
|
| :provided_code_does_not_match
|
||||||
| :invalid_client_secret
|
| :invalid_client_secret
|
||||||
| :app_token_not_found
|
| :invalid_or_expired
|
||||||
| any()}
|
| any()}
|
||||||
def generate_access_token(client_id, client_secret, code, redirect_uri) do
|
def generate_access_token(client_id, client_secret, code, redirect_uri, scope) do
|
||||||
with {:application,
|
with {:application,
|
||||||
%Application{
|
%Application{
|
||||||
id: application_id,
|
id: application_id,
|
||||||
client_secret: app_client_secret,
|
client_secret: app_client_secret,
|
||||||
scopes: scopes,
|
|
||||||
redirect_uris: redirect_uris
|
redirect_uris: redirect_uris
|
||||||
}} <-
|
}} <-
|
||||||
{:application, Applications.get_application_by_client_id(client_id)},
|
{:application, Applications.get_application_by_client_id(client_id)},
|
||||||
|
# TODO: check that app token scope still are acceptable
|
||||||
{:redirect_uri, true} <-
|
{:redirect_uri, true} <-
|
||||||
{:redirect_uri, redirect_uri in String.split(redirect_uris, "\n")},
|
{:redirect_uri, redirect_uri in redirect_uris},
|
||||||
{:app_token, %ApplicationToken{} = app_token} <-
|
{:app_token, %ApplicationToken{} = app_token} <-
|
||||||
{:app_token, Applications.get_application_token_by_authorization_code(code)},
|
{:app_token, Applications.get_application_token_by_authorization_code(code)},
|
||||||
{:ok, %ApplicationToken{application_id: application_id_from_token} = app_token} <-
|
{:ok, %ApplicationToken{application_id: application_id_from_token} = app_token} <-
|
||||||
|
@ -105,7 +141,7 @@ defmodule Mobilizon.Service.Auth.Applications do
|
||||||
expires_in: ttl_to_seconds(@app_access_tokens_ttl),
|
expires_in: ttl_to_seconds(@app_access_tokens_ttl),
|
||||||
refresh_token: refresh_token,
|
refresh_token: refresh_token,
|
||||||
refresh_token_expires_in: ttl_to_seconds(@app_refresh_tokens_ttl),
|
refresh_token_expires_in: ttl_to_seconds(@app_refresh_tokens_ttl),
|
||||||
scope: scopes,
|
scope: scope,
|
||||||
token_type: "bearer"
|
token_type: "bearer"
|
||||||
}}
|
}}
|
||||||
else
|
else
|
||||||
|
@ -122,15 +158,20 @@ defmodule Mobilizon.Service.Auth.Applications do
|
||||||
{:error, :redirect_uri_not_in_allowed}
|
{:error, :redirect_uri_not_in_allowed}
|
||||||
|
|
||||||
{:app_token, _} ->
|
{:app_token, _} ->
|
||||||
{:error, :app_token_not_found}
|
{:error, :invalid_or_expired}
|
||||||
|
|
||||||
{:error, err} ->
|
{:error, err} ->
|
||||||
{:error, err}
|
{:error, err}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_access_token(client_id, device_code) do
|
def generate_access_token_for_device_flow(client_id, device_code) do
|
||||||
case Applications.get_application_device_activation(client_id, device_code) do
|
Logger.debug("Generating access token for application device with",
|
||||||
|
client_id: client_id,
|
||||||
|
device_code: device_code
|
||||||
|
)
|
||||||
|
|
||||||
|
case Applications.get_application_device_activation_by_device_code(client_id, device_code) do
|
||||||
%ApplicationDeviceActivation{status: :success, scope: scope, user_id: user_id} =
|
%ApplicationDeviceActivation{status: :success, scope: scope, user_id: user_id} =
|
||||||
app_device_activation ->
|
app_device_activation ->
|
||||||
if device_activation_expired?(app_device_activation) do
|
if device_activation_expired?(app_device_activation) do
|
||||||
|
@ -164,13 +205,17 @@ defmodule Mobilizon.Service.Auth.Applications do
|
||||||
end
|
end
|
||||||
|
|
||||||
%ApplicationDeviceActivation{status: :incorrect_device_code} ->
|
%ApplicationDeviceActivation{status: :incorrect_device_code} ->
|
||||||
|
Logger.error("Incorrect device code set")
|
||||||
{:error, :incorrect_device_code}
|
{:error, :incorrect_device_code}
|
||||||
|
|
||||||
%ApplicationDeviceActivation{status: :access_denied} ->
|
%ApplicationDeviceActivation{status: :access_denied} ->
|
||||||
{:error, :access_denied}
|
{:error, :access_denied}
|
||||||
|
|
||||||
|
nil ->
|
||||||
|
Logger.error("nil returned")
|
||||||
|
{:error, :incorrect_device_code}
|
||||||
|
|
||||||
err ->
|
err ->
|
||||||
require Logger
|
|
||||||
Logger.error(inspect(err))
|
Logger.error(inspect(err))
|
||||||
{:error, :incorrect_device_code}
|
{:error, :incorrect_device_code}
|
||||||
end
|
end
|
||||||
|
@ -191,35 +236,42 @@ defmodule Mobilizon.Service.Auth.Applications do
|
||||||
|
|
||||||
@spec register_device_code(String.t(), String.t() | nil) ::
|
@spec register_device_code(String.t(), String.t() | nil) ::
|
||||||
{:ok, ApplicationDeviceActivation.t()}
|
{:ok, ApplicationDeviceActivation.t()}
|
||||||
|
| {:error, :application_not_found}
|
||||||
|
| {:error, :scope_not_included}
|
||||||
| {:error, Ecto.Changeset.t()}
|
| {:error, Ecto.Changeset.t()}
|
||||||
def register_device_code(client_id, scope) do
|
def register_device_code(client_id, scope) do
|
||||||
%Application{} = application = Applications.get_application_by_client_id(client_id)
|
with {:app, %Application{scope: app_scope} = application} <-
|
||||||
device_code = string_of_length(40)
|
{:app, Applications.get_application_by_client_id(client_id)},
|
||||||
user_code = string_of_length(8)
|
{device_code, user_code, verification_uri} <-
|
||||||
verification_uri = Routes.page_url(Mobilizon.Web.Endpoint, :auth_device)
|
{string_of_length(40), string_of_length(8),
|
||||||
expires_in = @expires_in
|
Routes.page_url(Mobilizon.Web.Endpoint, :auth_device)},
|
||||||
interval = @interval
|
{:scope_included, true} <- {:scope_included, request_scope_valid?(app_scope, scope)},
|
||||||
|
{:ok, %ApplicationDeviceActivation{} = application_device_activation} <-
|
||||||
case Applications.create_application_device_activation(%{
|
Applications.create_application_device_activation(%{
|
||||||
device_code: device_code,
|
device_code: device_code,
|
||||||
user_code: user_code,
|
user_code: user_code,
|
||||||
expires_in: expires_in,
|
expires_in: @expires_in,
|
||||||
application_id: application.id,
|
application_id: application.id,
|
||||||
scope: scope
|
scope: scope
|
||||||
}) do
|
}) do
|
||||||
{:ok, %ApplicationDeviceActivation{} = application_device_activation} ->
|
|
||||||
{:ok,
|
{:ok,
|
||||||
application_device_activation
|
application_device_activation
|
||||||
|> Map.from_struct()
|
|> Map.from_struct()
|
||||||
|> Map.take([:device_code, :user_code, :expires_in])
|
|> Map.take([:device_code, :user_code, :expires_in])
|
||||||
|> Map.update!(:user_code, &user_code_displayed/1)
|
|> Map.update!(:user_code, &user_code_displayed/1)
|
||||||
|> Map.merge(%{
|
|> Map.merge(%{
|
||||||
interval: interval,
|
interval: @interval,
|
||||||
verification_uri: verification_uri
|
verification_uri: verification_uri
|
||||||
})}
|
})}
|
||||||
|
else
|
||||||
{:error, %Ecto.Changeset{} = err} ->
|
{:error, %Ecto.Changeset{} = err} ->
|
||||||
{:error, err}
|
{:error, err}
|
||||||
|
|
||||||
|
{:app, nil} ->
|
||||||
|
{:error, :application_not_found}
|
||||||
|
|
||||||
|
{:scope_included, false} ->
|
||||||
|
{:error, :scope_not_included}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -245,6 +297,45 @@ defmodule Mobilizon.Service.Auth.Applications do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec refresh_tokens(String.t(), String.t(), String.t()) ::
|
||||||
|
{:ok, access_token_details()}
|
||||||
|
| {:error, :invalid_client_credentials}
|
||||||
|
| {:error, :invalid_refresh_token}
|
||||||
|
| {:error, any()}
|
||||||
|
def refresh_tokens(refresh_token, user_client_id, user_client_secret) do
|
||||||
|
with {:resource_from_token,
|
||||||
|
{:ok,
|
||||||
|
%ApplicationToken{
|
||||||
|
application: %Application{client_id: app_client_id, client_secret: app_client_secret},
|
||||||
|
scope: scope
|
||||||
|
} = app_token,
|
||||||
|
_claims}} <- {:resource_from_token, Guardian.resource_from_token(refresh_token)},
|
||||||
|
{:valid_client_credentials, true} <-
|
||||||
|
{:valid_client_credentials,
|
||||||
|
app_client_id == user_client_id and app_client_secret == user_client_secret},
|
||||||
|
{:ok, _old, {exchanged_token, _claims}} <-
|
||||||
|
Guardian.exchange(refresh_token, "refresh", "access", ttl: @app_access_tokens_ttl),
|
||||||
|
{:ok, new_refresh_token} <-
|
||||||
|
Authenticator.generate_refresh_token(app_token, @app_refresh_tokens_ttl),
|
||||||
|
{:ok, _claims} <- Guardian.revoke(refresh_token) do
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
access_token: exchanged_token,
|
||||||
|
expires_in: ttl_to_seconds(@app_access_tokens_ttl),
|
||||||
|
refresh_token: new_refresh_token,
|
||||||
|
refresh_token_expires_in: ttl_to_seconds(@app_refresh_tokens_ttl),
|
||||||
|
scope: scope,
|
||||||
|
token_type: "bearer"
|
||||||
|
}}
|
||||||
|
else
|
||||||
|
{:valid_client_credentials, false} ->
|
||||||
|
{:error, :invalid_client_credentials}
|
||||||
|
|
||||||
|
{:resource_from_token, _} ->
|
||||||
|
{:error, :invalid_refresh_token}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp user_code_displayed(user_code) do
|
defp user_code_displayed(user_code) do
|
||||||
String.slice(user_code, 0..3) <> "-" <> String.slice(user_code, 4..7)
|
String.slice(user_code, 0..3) <> "-" <> String.slice(user_code, 4..7)
|
||||||
end
|
end
|
||||||
|
@ -265,6 +356,12 @@ defmodule Mobilizon.Service.Auth.Applications do
|
||||||
expires_in: expires_in
|
expires_in: expires_in
|
||||||
}) do
|
}) do
|
||||||
NaiveDateTime.compare(NaiveDateTime.add(inserted_at, expires_in), NaiveDateTime.utc_now()) ==
|
NaiveDateTime.compare(NaiveDateTime.add(inserted_at, expires_in), NaiveDateTime.utc_now()) ==
|
||||||
:gt
|
:lt
|
||||||
|
end
|
||||||
|
|
||||||
|
defp request_scope_valid?(app_scope, request_scope) do
|
||||||
|
app_scopes = app_scope |> String.split(" ") |> MapSet.new()
|
||||||
|
request_scopes = request_scope |> String.split(" ") |> MapSet.new()
|
||||||
|
MapSet.subset?(request_scopes, app_scopes)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -38,7 +38,7 @@ defmodule Mobilizon.Web.Auth.Context do
|
||||||
|> set_app_token_context(context, app_token)
|
|> set_app_token_context(context, app_token)
|
||||||
|> set_user_context(user)
|
|> set_user_context(user)
|
||||||
|
|
||||||
nil ->
|
_ ->
|
||||||
{conn, context}
|
{conn, context}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,12 @@ defmodule Mobilizon.Web.Auth.ErrorHandler do
|
||||||
In case we have an auth error
|
In case we have an auth error
|
||||||
"""
|
"""
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
require Logger
|
||||||
|
|
||||||
# sobelow_skip ["XSS.SendResp"]
|
# sobelow_skip ["XSS.SendResp"]
|
||||||
@spec auth_error(Plug.Conn.t(), any(), any()) :: Plug.Conn.t()
|
@spec auth_error(Plug.Conn.t(), any(), any()) :: Plug.Conn.t()
|
||||||
def auth_error(conn, {type, _reason}, _opts) do
|
def auth_error(conn, {type, reason}, _opts) do
|
||||||
body = Jason.encode!(%{message: to_string(type)})
|
body = Jason.encode!(%{message: to_string(type), details: inspect(reason)})
|
||||||
send_resp(conn, 401, body)
|
send_resp(conn, 401, body)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -43,7 +43,9 @@ defmodule Mobilizon.Web.Auth.Guardian do
|
||||||
{:error, :invalid_id}
|
{:error, :invalid_id}
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
Ecto.NoResultsError -> {:error, :no_result}
|
e in Ecto.NoResultsError ->
|
||||||
|
Logger.warn("Received token claim for non existing user: #{inspect(e)}")
|
||||||
|
{:error, :no_result}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -62,7 +64,9 @@ defmodule Mobilizon.Web.Auth.Guardian do
|
||||||
{:error, :invalid_id}
|
{:error, :invalid_id}
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
Ecto.NoResultsError -> {:error, :no_result}
|
e in Ecto.NoResultsError ->
|
||||||
|
Logger.info("Received token claim for non existing app token: #{inspect(e.message)}")
|
||||||
|
{:error, :no_result}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -79,6 +83,8 @@ defmodule Mobilizon.Web.Auth.Guardian do
|
||||||
|
|
||||||
@spec on_verify(any(), any(), any()) :: {:ok, map()} | {:error, :token_not_found}
|
@spec on_verify(any(), any(), any()) :: {:ok, map()} | {:error, :token_not_found}
|
||||||
def on_verify(claims, token, _options) do
|
def on_verify(claims, token, _options) do
|
||||||
|
Logger.debug("[Guardian] Called on_verify")
|
||||||
|
|
||||||
with {:ok, _} <- Guardian.DB.on_verify(claims, token) do
|
with {:ok, _} <- Guardian.DB.on_verify(claims, token) do
|
||||||
{:ok, claims}
|
{:ok, claims}
|
||||||
end
|
end
|
||||||
|
@ -86,6 +92,8 @@ defmodule Mobilizon.Web.Auth.Guardian do
|
||||||
|
|
||||||
@spec on_revoke(any(), any(), any()) :: {:ok, map()} | {:error, :could_not_revoke_token}
|
@spec on_revoke(any(), any(), any()) :: {:ok, map()} | {:error, :could_not_revoke_token}
|
||||||
def on_revoke(claims, token, _options) do
|
def on_revoke(claims, token, _options) do
|
||||||
|
Logger.debug("[Guardian] Called on_revoke")
|
||||||
|
|
||||||
with {:ok, _} <- Guardian.DB.on_revoke(claims, token) do
|
with {:ok, _} <- Guardian.DB.on_revoke(claims, token) do
|
||||||
{:ok, claims}
|
{:ok, claims}
|
||||||
end
|
end
|
||||||
|
@ -94,6 +102,8 @@ defmodule Mobilizon.Web.Auth.Guardian do
|
||||||
@spec on_refresh({any(), any()}, {any(), any()}, any()) ::
|
@spec on_refresh({any(), any()}, {any(), any()}, any()) ::
|
||||||
{:ok, {String.t(), map()}, {String.t(), map()}} | {:error, any()}
|
{:ok, {String.t(), map()}, {String.t(), map()}} | {:error, any()}
|
||||||
def on_refresh({old_token, old_claims}, {new_token, new_claims}, _options) do
|
def on_refresh({old_token, old_claims}, {new_token, new_claims}, _options) do
|
||||||
|
Logger.debug("[Guardian] Called on_refresh")
|
||||||
|
|
||||||
with {:ok, _, _} <- Guardian.DB.on_refresh({old_token, old_claims}, {new_token, new_claims}) do
|
with {:ok, _, _} <- Guardian.DB.on_refresh({old_token, old_claims}, {new_token, new_claims}) do
|
||||||
{:ok, {old_token, old_claims}, {new_token, new_claims}}
|
{:ok, {old_token, old_claims}, {new_token, new_claims}}
|
||||||
end
|
end
|
||||||
|
@ -101,7 +111,10 @@ defmodule Mobilizon.Web.Auth.Guardian do
|
||||||
|
|
||||||
@spec on_exchange(any(), any(), any()) ::
|
@spec on_exchange(any(), any(), any()) ::
|
||||||
{:ok, {String.t(), map()}, {String.t(), map()}} | {:error, any()}
|
{:ok, {String.t(), map()}, {String.t(), map()}} | {:error, any()}
|
||||||
def on_exchange(old_stuff, new_stuff, options), do: on_refresh(old_stuff, new_stuff, options)
|
def on_exchange(old_stuff, new_stuff, options) do
|
||||||
|
Logger.debug("[Guardian] Called on_exchange")
|
||||||
|
on_refresh(old_stuff, new_stuff, options)
|
||||||
|
end
|
||||||
|
|
||||||
# def build_claims(claims, _resource, opts) do
|
# def build_claims(claims, _resource, opts) do
|
||||||
# claims = claims
|
# claims = claims
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
defmodule Mobilizon.Web.ApplicationController do
|
defmodule Mobilizon.Web.ApplicationController do
|
||||||
use Mobilizon.Web, :controller
|
use Mobilizon.Web, :controller
|
||||||
|
|
||||||
alias Mobilizon.Applications.{Application, ApplicationDeviceActivation}
|
alias Mobilizon.Applications.Application
|
||||||
alias Mobilizon.Service.Auth.Applications
|
alias Mobilizon.Service.Auth.Applications
|
||||||
plug(:put_layout, false)
|
plug(:put_layout, false)
|
||||||
import Mobilizon.Web.Gettext, only: [dgettext: 2]
|
import Mobilizon.Web.Gettext, only: [dgettext: 2]
|
||||||
|
@ -11,11 +11,14 @@ defmodule Mobilizon.Web.ApplicationController do
|
||||||
Create an application
|
Create an application
|
||||||
"""
|
"""
|
||||||
@spec create_application(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
@spec create_application(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||||
def create_application(conn, %{"name" => name, "redirect_uris" => redirect_uris} = args) do
|
def create_application(
|
||||||
|
conn,
|
||||||
|
%{"name" => name, "redirect_uris" => redirect_uris, "scope" => scope} = args
|
||||||
|
) do
|
||||||
case Applications.create(
|
case Applications.create(
|
||||||
name,
|
name,
|
||||||
redirect_uris,
|
String.split(redirect_uris, "\n"),
|
||||||
Map.get(args, "scopes"),
|
scope,
|
||||||
Map.get(args, "website")
|
Map.get(args, "website")
|
||||||
) do
|
) do
|
||||||
{:ok, %Application{} = app} ->
|
{:ok, %Application{} = app} ->
|
||||||
|
@ -24,7 +27,19 @@ defmodule Mobilizon.Web.ApplicationController do
|
||||||
Map.take(app, [:name, :website, :redirect_uris, :client_id, :client_secret, :scope])
|
Map.take(app, [:name, :website, :redirect_uris, :client_id, :client_secret, :scope])
|
||||||
)
|
)
|
||||||
|
|
||||||
{:error, _error} ->
|
{:error, :invalid_scope} ->
|
||||||
|
send_resp(
|
||||||
|
conn,
|
||||||
|
400,
|
||||||
|
dgettext(
|
||||||
|
"errors",
|
||||||
|
"The scope parameter is not a space separated list of valid scopes"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
{:error, error} ->
|
||||||
|
Logger.error(inspect(error))
|
||||||
|
|
||||||
send_resp(
|
send_resp(
|
||||||
conn,
|
conn,
|
||||||
500,
|
500,
|
||||||
|
@ -42,7 +57,7 @@ defmodule Mobilizon.Web.ApplicationController do
|
||||||
400,
|
400,
|
||||||
dgettext(
|
dgettext(
|
||||||
"errors",
|
"errors",
|
||||||
"Both name and redirect_uri parameters are required to create an application"
|
"All of name, scope and redirect_uri parameters are required to create an application"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -60,14 +75,15 @@ defmodule Mobilizon.Web.ApplicationController do
|
||||||
client_id = conn.query_params["client_id"]
|
client_id = conn.query_params["client_id"]
|
||||||
redirect_uri = conn.query_params["redirect_uri"]
|
redirect_uri = conn.query_params["redirect_uri"]
|
||||||
state = conn.query_params["state"]
|
state = conn.query_params["state"]
|
||||||
|
scope = conn.query_params["scope"]
|
||||||
|
|
||||||
if is_binary(client_id) and is_binary(redirect_uri) and is_binary(state) do
|
if is_binary(client_id) and is_binary(redirect_uri) and is_binary(state) and is_binary(scope) do
|
||||||
redirect(conn,
|
redirect(conn,
|
||||||
to:
|
to:
|
||||||
Routes.page_path(conn, :authorize,
|
Routes.page_path(conn, :authorize,
|
||||||
client_id: client_id,
|
client_id: client_id,
|
||||||
redirect_uri: redirect_uri,
|
redirect_uri: redirect_uri,
|
||||||
scope: conn.query_params["scope"],
|
scope: scope,
|
||||||
state: state
|
state: state
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -77,14 +93,14 @@ defmodule Mobilizon.Web.ApplicationController do
|
||||||
400,
|
400,
|
||||||
dgettext(
|
dgettext(
|
||||||
"errors",
|
"errors",
|
||||||
"You need to specify client_id, redirect_uri and state to autorize an application"
|
"You need to specify client_id, redirect_uri, scope and state to autorize an application"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def device_code(conn, %{"client_id" => client_id} = args) do
|
def device_code(conn, %{"client_id" => client_id, "scope" => scope}) do
|
||||||
case Applications.register_device_code(client_id, Map.get(args, "scope")) do
|
case Applications.register_device_code(client_id, scope) do
|
||||||
{:ok, res} when is_map(res) ->
|
{:ok, res} when is_map(res) ->
|
||||||
case get_format(conn) do
|
case get_format(conn) do
|
||||||
"json" ->
|
"json" ->
|
||||||
|
@ -94,6 +110,12 @@ defmodule Mobilizon.Web.ApplicationController do
|
||||||
send_resp(conn, 200, URI.encode_query(res))
|
send_resp(conn, 200, URI.encode_query(res))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
{:error, :scope_not_included} ->
|
||||||
|
send_resp(conn, 400, "The given scope is not in the list of the app declared scopes")
|
||||||
|
|
||||||
|
{:error, :application_not_found} ->
|
||||||
|
send_resp(conn, 400, "No application with this client_id was found")
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{} = err} ->
|
{:error, %Ecto.Changeset{} = err} ->
|
||||||
Logger.error(inspect(err))
|
Logger.error(inspect(err))
|
||||||
send_resp(conn, 500, "Unable to produce device code")
|
send_resp(conn, 500, "Unable to produce device code")
|
||||||
|
@ -101,7 +123,11 @@ defmodule Mobilizon.Web.ApplicationController do
|
||||||
end
|
end
|
||||||
|
|
||||||
def device_code(conn, _args) do
|
def device_code(conn, _args) do
|
||||||
send_resp(conn, 400, "You need to send to send at least client_id to obtain a device code")
|
send_resp(
|
||||||
|
conn,
|
||||||
|
400,
|
||||||
|
"You need to pass both client_id and scope as parameters to obtain a device code"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec generate_access_token(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
@spec generate_access_token(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||||
|
@ -109,59 +135,106 @@ defmodule Mobilizon.Web.ApplicationController do
|
||||||
"client_id" => client_id,
|
"client_id" => client_id,
|
||||||
"client_secret" => client_secret,
|
"client_secret" => client_secret,
|
||||||
"code" => code,
|
"code" => code,
|
||||||
"redirect_uri" => redirect_uri
|
"redirect_uri" => redirect_uri,
|
||||||
|
"scope" => scope,
|
||||||
|
"grant_type" => "authorization_code"
|
||||||
}) do
|
}) do
|
||||||
case Applications.generate_access_token(client_id, client_secret, code, redirect_uri) do
|
case do_generate_access_token(client_id, client_secret, code, redirect_uri, scope) do
|
||||||
{:ok, token} ->
|
{:ok, token} ->
|
||||||
redirect(conn, external: generate_redirect_with_query_params(redirect_uri, token))
|
json(conn, token)
|
||||||
|
|
||||||
{:error, :application_not_found} ->
|
{:error, msg} ->
|
||||||
send_resp(conn, 400, dgettext("errors", "No application was found with this client_id"))
|
Logger.debug(msg)
|
||||||
|
json(conn, %{error: true, details: msg})
|
||||||
{:error, :redirect_uri_not_in_allowed} ->
|
|
||||||
send_resp(conn, 400, dgettext("errors", "This redirect URI is not allowed"))
|
|
||||||
|
|
||||||
{:error, :invalid_or_expired} ->
|
|
||||||
send_resp(conn, 400, dgettext("errors", "The provided code is invalid or expired"))
|
|
||||||
|
|
||||||
{:error, :invalid_client_id} ->
|
|
||||||
send_resp(
|
|
||||||
conn,
|
|
||||||
400,
|
|
||||||
dgettext("errors", "The provided client_id does not match the provided code")
|
|
||||||
)
|
|
||||||
|
|
||||||
{:error, :invalid_client_secret} ->
|
|
||||||
send_resp(conn, 400, dgettext("errors", "The provided client_secret is invalid"))
|
|
||||||
|
|
||||||
{:error, :user_not_found} ->
|
|
||||||
send_resp(conn, 400, dgettext("errors", "The user for this code was not found"))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_access_token(conn, %{
|
|
||||||
"client_id" => client_id,
|
|
||||||
"device_code" => device_code,
|
|
||||||
"grant_type" => "urn:ietf:params:oauth:grant-type:device_code",
|
|
||||||
"_format" => "json"
|
|
||||||
}) do
|
|
||||||
json(conn, Applications.generate_access_token(client_id, device_code))
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate_access_token(conn, %{
|
def generate_access_token(conn, %{
|
||||||
"client_id" => client_id,
|
"client_id" => client_id,
|
||||||
"device_code" => device_code,
|
"device_code" => device_code,
|
||||||
"grant_type" => "urn:ietf:params:oauth:grant-type:device_code"
|
"grant_type" => "urn:ietf:params:oauth:grant-type:device_code"
|
||||||
}) do
|
}) do
|
||||||
|
case Applications.generate_access_token_for_device_flow(client_id, device_code) do
|
||||||
|
{:ok, res} ->
|
||||||
|
case get_format(conn) do
|
||||||
|
"json" ->
|
||||||
|
json(conn, res)
|
||||||
|
|
||||||
|
_ ->
|
||||||
send_resp(
|
send_resp(
|
||||||
conn,
|
conn,
|
||||||
200,
|
200,
|
||||||
URI.encode_query(Applications.generate_access_token(client_id, device_code))
|
URI.encode_query(res)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec generate_redirect_with_query_params(String.t(), map()) :: String.t()
|
{:error, :incorrect_device_code} ->
|
||||||
defp generate_redirect_with_query_params(redirect_uri, query_params) do
|
send_resp(conn, 400, "The client_id provided or the device_code associated is invalid")
|
||||||
redirect_uri |> URI.parse() |> URI.merge("?" <> URI.encode_query(query_params)) |> to_string()
|
|
||||||
|
{:error, :access_denied} ->
|
||||||
|
send_resp(conn, 401, "The user rejected the requested authorization")
|
||||||
|
|
||||||
|
{:error, :expired} ->
|
||||||
|
send_resp(conn, 400, "The given device_code has expired")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_access_token(conn, %{
|
||||||
|
"refresh_token" => refresh_token,
|
||||||
|
"grant_type" => "refresh_token",
|
||||||
|
"client_id" => client_id,
|
||||||
|
"client_secret" => client_secret
|
||||||
|
}) do
|
||||||
|
case Applications.refresh_tokens(refresh_token, client_id, client_secret) do
|
||||||
|
{:ok, res} ->
|
||||||
|
json(conn, res)
|
||||||
|
|
||||||
|
{:error, :invalid_client_credentials} ->
|
||||||
|
send_resp(conn, 400, "Invalid client credentials provided")
|
||||||
|
|
||||||
|
{:error, :invalid_refresh_token} ->
|
||||||
|
send_resp(conn, 400, "Invalid refresh token provided")
|
||||||
|
|
||||||
|
{:error, err} when is_atom(err) ->
|
||||||
|
send_resp(conn, 500, to_string(err))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_access_token(conn, _args) do
|
||||||
|
send_resp(
|
||||||
|
conn,
|
||||||
|
400,
|
||||||
|
"Incorrect parameters sent. You need to provide at least the grant_type and client_id parameters, depending on the grant type being used."
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec do_generate_access_token(String.t(), String.t(), String.t(), String.t(), String.t()) ::
|
||||||
|
{:ok, Applications.access_token_details()} | {:error, String.t()}
|
||||||
|
defp do_generate_access_token(client_id, client_secret, code, redirect_uri, scope) do
|
||||||
|
case Applications.generate_access_token(
|
||||||
|
client_id,
|
||||||
|
client_secret,
|
||||||
|
code,
|
||||||
|
redirect_uri,
|
||||||
|
scope
|
||||||
|
) do
|
||||||
|
{:ok, token} ->
|
||||||
|
{:ok, token}
|
||||||
|
|
||||||
|
{:error, :application_not_found} ->
|
||||||
|
{:error, dgettext("errors", "No application was found with this client_id")}
|
||||||
|
|
||||||
|
{:error, :redirect_uri_not_in_allowed} ->
|
||||||
|
{:error, dgettext("errors", "This redirect URI is not allowed")}
|
||||||
|
|
||||||
|
{:error, :invalid_or_expired} ->
|
||||||
|
{:error, dgettext("errors", "The provided code is invalid or expired")}
|
||||||
|
|
||||||
|
{:error, :provided_code_does_not_match} ->
|
||||||
|
{:error, dgettext("errors", "The provided client_id does not match the provided code")}
|
||||||
|
|
||||||
|
{:error, :invalid_client_secret} ->
|
||||||
|
{:error, dgettext("errors", "The provided client_secret is invalid")}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -208,7 +208,6 @@ defmodule Mobilizon.Web.Router do
|
||||||
|
|
||||||
post("/apps", ApplicationController, :create_application)
|
post("/apps", ApplicationController, :create_application)
|
||||||
get("/oauth/authorize", ApplicationController, :authorize)
|
get("/oauth/authorize", ApplicationController, :authorize)
|
||||||
post("/oauth/token", ApplicationController, :generate_access_token)
|
|
||||||
get("/oauth/autorize_approve", PageController, :authorize)
|
get("/oauth/autorize_approve", PageController, :authorize)
|
||||||
get("/login/device", PageController, :auth_device)
|
get("/login/device", PageController, :auth_device)
|
||||||
end
|
end
|
||||||
|
@ -217,10 +216,11 @@ defmodule Mobilizon.Web.Router do
|
||||||
plug(:accepts, ["html", "json"])
|
plug(:accepts, ["html", "json"])
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/login", Mobilizon.Web do
|
scope "/", Mobilizon.Web do
|
||||||
pipe_through(:login)
|
pipe_through(:login)
|
||||||
|
|
||||||
post("/device/code", ApplicationController, :device_code)
|
post("/login/device/code", ApplicationController, :device_code)
|
||||||
|
post("/oauth/token", ApplicationController, :generate_access_token)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/proxy/", Mobilizon.Web do
|
scope "/proxy/", Mobilizon.Web do
|
||||||
|
|
1
mix.exs
1
mix.exs
|
@ -209,6 +209,7 @@ defmodule Mobilizon.Mixfile do
|
||||||
{:unplug, "~> 1.0.0"},
|
{:unplug, "~> 1.0.0"},
|
||||||
{:replug, "~> 0.1.0"},
|
{:replug, "~> 0.1.0"},
|
||||||
{:exkismet, github: "tcitworld/exkismet"},
|
{:exkismet, github: "tcitworld/exkismet"},
|
||||||
|
{:rajska, github: "churcho/rajska", branch: "fix/update-absinthe"},
|
||||||
# Dev and test dependencies
|
# Dev and test dependencies
|
||||||
{:phoenix_live_reload, "~> 1.2", only: [:dev, :e2e]},
|
{:phoenix_live_reload, "~> 1.2", only: [:dev, :e2e]},
|
||||||
{:ex_machina, "~> 2.3", only: [:dev, :test]},
|
{:ex_machina, "~> 2.3", only: [:dev, :test]},
|
||||||
|
|
1
mix.lock
1
mix.lock
|
@ -118,6 +118,7 @@
|
||||||
"plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
|
"plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
|
||||||
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
|
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
|
||||||
"progress_bar": {:hex, :progress_bar, "2.0.1", "7b40200112ae533d5adceb80ff75fbe66dc753bca5f6c55c073bfc122d71896d", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "2519eb58a2f149a3a094e729378256d8cb6d96a259ec94841bd69fdc71f18f87"},
|
"progress_bar": {:hex, :progress_bar, "2.0.1", "7b40200112ae533d5adceb80ff75fbe66dc753bca5f6c55c073bfc122d71896d", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "2519eb58a2f149a3a094e729378256d8cb6d96a259ec94841bd69fdc71f18f87"},
|
||||||
|
"rajska": {:git, "https://github.com/churcho/rajska.git", "5da424969d5f40dcab690d3a25b248f85f712823", [branch: "fix/update-absinthe"]},
|
||||||
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
|
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
|
||||||
"remote_ip": {:hex, :remote_ip, "1.1.0", "cb308841595d15df3f9073b7c39243a1dd6ca56e5020295cb012c76fbec50f2d", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "616ffdf66aaad6a72fc546dabf42eed87e2a99e97b09cbd92b10cc180d02ed74"},
|
"remote_ip": {:hex, :remote_ip, "1.1.0", "cb308841595d15df3f9073b7c39243a1dd6ca56e5020295cb012c76fbec50f2d", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "616ffdf66aaad6a72fc546dabf42eed87e2a99e97b09cbd92b10cc180d02ed74"},
|
||||||
"replug": {:hex, :replug, "0.1.0", "61d35f8c873c0078a23c49579a48f36e45789414b1ec0daee3fd5f4e34221f23", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f71f7a57e944e854fe4946060c6964098e53958074c69fb844b96e0bd58cfa60"},
|
"replug": {:hex, :replug, "0.1.0", "61d35f8c873c0078a23c49579a48f36e45789414b1ec0daee3fd5f4e34221f23", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f71f7a57e944e854fe4946060c6964098e53958074c69fb844b96e0bd58cfa60"},
|
||||||
|
|
|
@ -6,8 +6,8 @@ defmodule Mobilizon.Repo.Migrations.CreateApplications do
|
||||||
add(:name, :string, null: false)
|
add(:name, :string, null: false)
|
||||||
add(:client_id, :string, null: false)
|
add(:client_id, :string, null: false)
|
||||||
add(:client_secret, :string, null: false)
|
add(:client_secret, :string, null: false)
|
||||||
add(:redirect_uris, :string, null: false)
|
add(:redirect_uris, {:array, :string}, null: false)
|
||||||
add(:scopes, :string, null: true)
|
add(:scope, :string, null: true)
|
||||||
add(:website, :string, null: true)
|
add(:website, :string, null: true)
|
||||||
add(:owner_type, :string, null: true)
|
add(:owner_type, :string, null: true)
|
||||||
add(:owner_id, :integer, null: true)
|
add(:owner_id, :integer, null: true)
|
||||||
|
|
|
@ -6,6 +6,8 @@ defmodule Mobilizon.Repo.Migrations.CreateApplicationTokens do
|
||||||
add(:user_id, references(:users, on_delete: :delete_all), null: false)
|
add(:user_id, references(:users, on_delete: :delete_all), null: false)
|
||||||
add(:application_id, references(:applications, on_delete: :delete_all), null: false)
|
add(:application_id, references(:applications, on_delete: :delete_all), null: false)
|
||||||
add(:authorization_code, :string, null: true)
|
add(:authorization_code, :string, null: true)
|
||||||
|
add(:status, :string, default: "pending", null: false)
|
||||||
|
add(:scope, :string)
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
defmodule Mobilizon.Storage.Repo.Migrations.AddDeviceFlowSupport do
|
|
||||||
use Ecto.Migration
|
|
||||||
|
|
||||||
def change do
|
|
||||||
alter table(:application_tokens) do
|
|
||||||
add(:status, :string, default: :pending, null: false)
|
|
||||||
add(:scope, :string)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -125,7 +125,8 @@ defmodule Mobilizon.GraphQL.Resolvers.ActivityTest do
|
||||||
variables: %{preferredUsername: preferred_username}
|
variables: %{preferredUsername: preferred_username}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "unauthenticated"
|
assert "Not authorized to access object paginated_activity_list" ==
|
||||||
|
hd(res["errors"])["message"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "without being a member", %{
|
test "without being a member", %{
|
||||||
|
|
|
@ -12,23 +12,8 @@ defmodule Mobilizon.GraphQL.Resolvers.AdminTest do
|
||||||
alias Mobilizon.GraphQL.{AbsintheHelpers, API}
|
alias Mobilizon.GraphQL.{AbsintheHelpers, API}
|
||||||
|
|
||||||
describe "Resolver: List the action logs" do
|
describe "Resolver: List the action logs" do
|
||||||
@note_content "This a note on a report"
|
@action_logs_query """
|
||||||
test "list_action_logs/3 list action logs", %{conn: conn} do
|
query ActionLogs {
|
||||||
%User{} = user_moderator = insert(:user, role: :moderator)
|
|
||||||
%Actor{} = moderator = insert(:actor, user: user_moderator)
|
|
||||||
|
|
||||||
%User{} = user_moderator_2 = insert(:user, role: :moderator)
|
|
||||||
%Actor{} = moderator_2 = insert(:actor, user: user_moderator_2)
|
|
||||||
|
|
||||||
%Report{} = report = insert(:report)
|
|
||||||
API.Reports.update_report_status(moderator, report, "resolved")
|
|
||||||
|
|
||||||
{:ok, %Note{} = note} = API.Reports.create_report_note(report, moderator_2, @note_content)
|
|
||||||
|
|
||||||
API.Reports.delete_report_note(note, moderator_2)
|
|
||||||
|
|
||||||
query = """
|
|
||||||
{
|
|
||||||
actionLogs {
|
actionLogs {
|
||||||
total
|
total
|
||||||
elements {
|
elements {
|
||||||
|
@ -50,24 +35,36 @@ defmodule Mobilizon.GraphQL.Resolvers.AdminTest do
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
res =
|
@note_content "This a note on a report"
|
||||||
conn
|
test "list_action_logs/3 list action logs", %{conn: conn} do
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "actionLogs"))
|
%User{} = user_moderator = insert(:user, role: :moderator)
|
||||||
|
%Actor{} = moderator = insert(:actor, user: user_moderator)
|
||||||
|
|
||||||
assert json_response(res, 200)["errors"] |> hd |> Map.get("message") ==
|
%User{} = user_moderator_2 = insert(:user, role: :moderator)
|
||||||
"You need to be logged-in and a moderator to list action logs"
|
%Actor{} = moderator_2 = insert(:actor, user: user_moderator_2)
|
||||||
|
|
||||||
|
%Report{} = report = insert(:report)
|
||||||
|
API.Reports.update_report_status(moderator, report, "resolved")
|
||||||
|
|
||||||
|
{:ok, %Note{} = note} = API.Reports.create_report_note(report, moderator_2, @note_content)
|
||||||
|
|
||||||
|
API.Reports.delete_report_note(note, moderator_2)
|
||||||
|
res = AbsintheHelpers.graphql_query(conn, query: @action_logs_query)
|
||||||
|
|
||||||
|
assert res["errors"] |> hd |> Map.get("message") ==
|
||||||
|
"You need to be logged in"
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> auth_conn(user_moderator)
|
|> auth_conn(user_moderator)
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "actionLogs"))
|
|> AbsintheHelpers.graphql_query(query: @action_logs_query)
|
||||||
|
|
||||||
assert json_response(res, 200)["errors"] == nil
|
assert is_nil(res["errors"])
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["actionLogs"]["total"] == 3
|
assert res["data"]["actionLogs"]["total"] == 3
|
||||||
assert json_response(res, 200)["data"]["actionLogs"]["elements"] |> length == 3
|
assert res["data"]["actionLogs"]["elements"] |> length == 3
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["actionLogs"]["elements"] == [
|
assert res["data"]["actionLogs"]["elements"] == [
|
||||||
%{
|
%{
|
||||||
"action" => "NOTE_DELETION",
|
"action" => "NOTE_DELETION",
|
||||||
"actor" => %{"preferredUsername" => moderator_2.preferred_username},
|
"actor" => %{"preferredUsername" => moderator_2.preferred_username},
|
||||||
|
@ -88,13 +85,8 @@ defmodule Mobilizon.GraphQL.Resolvers.AdminTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "Resolver: Get the dashboard statistics" do
|
describe "Resolver: Get the dashboard statistics" do
|
||||||
test "get_dashboard/3 gets dashboard information", %{conn: conn} do
|
@dashbord_information_query """
|
||||||
%Event{title: title} = insert(:event)
|
query Dashboard {
|
||||||
|
|
||||||
%User{} = user_admin = insert(:user, role: :administrator)
|
|
||||||
|
|
||||||
query = """
|
|
||||||
{
|
|
||||||
dashboard {
|
dashboard {
|
||||||
lastPublicEventPublished {
|
lastPublicEventPublished {
|
||||||
title
|
title
|
||||||
|
@ -107,22 +99,23 @@ defmodule Mobilizon.GraphQL.Resolvers.AdminTest do
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
res =
|
test "get_dashboard/3 gets dashboard information", %{conn: conn} do
|
||||||
conn
|
%Event{title: title} = insert(:event)
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "actionLogs"))
|
|
||||||
|
|
||||||
assert json_response(res, 200)["errors"] |> hd |> Map.get("message") ==
|
%User{} = user_admin = insert(:user, role: :administrator)
|
||||||
"You need to be logged-in and an administrator to access dashboard statistics"
|
|
||||||
|
res = AbsintheHelpers.graphql_query(conn, query: @dashbord_information_query)
|
||||||
|
|
||||||
|
assert res["errors"] |> hd |> Map.get("message") == "You need to be logged in"
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> auth_conn(user_admin)
|
|> auth_conn(user_admin)
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "actionLogs"))
|
|> AbsintheHelpers.graphql_query(query: @dashbord_information_query)
|
||||||
|
|
||||||
assert json_response(res, 200)["errors"] == nil
|
assert is_nil(res["errors"])
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["dashboard"]["lastPublicEventPublished"]["title"] ==
|
assert title == res["data"]["dashboard"]["lastPublicEventPublished"]["title"]
|
||||||
title
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -175,7 +168,6 @@ defmodule Mobilizon.GraphQL.Resolvers.AdminTest do
|
||||||
|> AbsintheHelpers.graphql_query(query: @relay_followers_query)
|
|> AbsintheHelpers.graphql_query(query: @relay_followers_query)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "You need to be logged in"
|
assert hd(res["errors"])["message"] == "You need to be logged in"
|
||||||
assert hd(res["errors"])["status_code"] == 401
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "test list_relay_followers/3 returns nothing when not an admin", %{conn: conn} do
|
test "test list_relay_followers/3 returns nothing when not an admin", %{conn: conn} do
|
||||||
|
@ -200,7 +192,6 @@ defmodule Mobilizon.GraphQL.Resolvers.AdminTest do
|
||||||
|> AbsintheHelpers.graphql_query(query: @relay_followers_query)
|
|> AbsintheHelpers.graphql_query(query: @relay_followers_query)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "You don't have permission to do this"
|
assert hd(res["errors"])["message"] == "You don't have permission to do this"
|
||||||
assert hd(res["errors"])["status_code"] == 403
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|
@ -208,7 +199,6 @@ defmodule Mobilizon.GraphQL.Resolvers.AdminTest do
|
||||||
|> AbsintheHelpers.graphql_query(query: @relay_followers_query)
|
|> AbsintheHelpers.graphql_query(query: @relay_followers_query)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "You don't have permission to do this"
|
assert hd(res["errors"])["message"] == "You don't have permission to do this"
|
||||||
assert hd(res["errors"])["status_code"] == 403
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "test list_relay_followers/3 returns relay followers", %{conn: conn} do
|
test "test list_relay_followers/3 returns relay followers", %{conn: conn} do
|
||||||
|
@ -258,7 +248,6 @@ defmodule Mobilizon.GraphQL.Resolvers.AdminTest do
|
||||||
|> AbsintheHelpers.graphql_query(query: @relay_followings_query)
|
|> AbsintheHelpers.graphql_query(query: @relay_followings_query)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "You need to be logged in"
|
assert hd(res["errors"])["message"] == "You need to be logged in"
|
||||||
assert hd(res["errors"])["status_code"] == 401
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "test list_relay_followings/3 returns nothing when not an admin", %{conn: conn} do
|
test "test list_relay_followings/3 returns nothing when not an admin", %{conn: conn} do
|
||||||
|
@ -284,7 +273,6 @@ defmodule Mobilizon.GraphQL.Resolvers.AdminTest do
|
||||||
|> AbsintheHelpers.graphql_query(query: @relay_followings_query)
|
|> AbsintheHelpers.graphql_query(query: @relay_followings_query)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "You don't have permission to do this"
|
assert hd(res["errors"])["message"] == "You don't have permission to do this"
|
||||||
assert hd(res["errors"])["status_code"] == 403
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|
@ -292,7 +280,6 @@ defmodule Mobilizon.GraphQL.Resolvers.AdminTest do
|
||||||
|> AbsintheHelpers.graphql_query(query: @relay_followings_query)
|
|> AbsintheHelpers.graphql_query(query: @relay_followings_query)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "You don't have permission to do this"
|
assert hd(res["errors"])["message"] == "You don't have permission to do this"
|
||||||
assert hd(res["errors"])["status_code"] == 403
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "test list_relay_followings/3 returns relay followings", %{conn: conn} do
|
test "test list_relay_followings/3 returns relay followings", %{conn: conn} do
|
||||||
|
@ -403,7 +390,7 @@ defmodule Mobilizon.GraphQL.Resolvers.AdminTest do
|
||||||
|> AbsintheHelpers.graphql_query(query: @admin_settings_query)
|
|> AbsintheHelpers.graphql_query(query: @admin_settings_query)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] ==
|
assert hd(res["errors"])["message"] ==
|
||||||
"You need to be logged-in and an administrator to access admin settings"
|
"You don't have permission to do this"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -490,7 +477,7 @@ defmodule Mobilizon.GraphQL.Resolvers.AdminTest do
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] ==
|
assert hd(res["errors"])["message"] ==
|
||||||
"You need to be logged-in and an administrator to save admin settings"
|
"You don't have permission to do this"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -524,7 +511,7 @@ defmodule Mobilizon.GraphQL.Resolvers.AdminTest do
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] ==
|
assert hd(res["errors"])["message"] ==
|
||||||
"You need to be logged-in and an administrator to edit an user's details"
|
"You don't have permission to do this"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "when putting same email", %{conn: conn, user: user, admin: admin} do
|
test "when putting same email", %{conn: conn, user: user, admin: admin} do
|
||||||
|
|
533
test/graphql/resolvers/application_test.exs
Normal file
533
test/graphql/resolvers/application_test.exs
Normal file
|
@ -0,0 +1,533 @@
|
||||||
|
defmodule Mobilizon.GraphQL.Resolvers.ApplicationTest do
|
||||||
|
use Mobilizon.Web.ConnCase
|
||||||
|
|
||||||
|
import Mobilizon.Factory
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
alias Mobilizon.Applications.{Application, ApplicationDeviceActivation}
|
||||||
|
alias Mobilizon.GraphQL.AbsintheHelpers
|
||||||
|
|
||||||
|
@identities_query """
|
||||||
|
query LoggedUser {
|
||||||
|
loggedUser {
|
||||||
|
actors {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
describe "Authorize an application" do
|
||||||
|
@authorize_mutation """
|
||||||
|
mutation AuthorizeApplication(
|
||||||
|
$applicationClientId: String!
|
||||||
|
$redirectURI: String!
|
||||||
|
$state: String
|
||||||
|
$scope: String!
|
||||||
|
) {
|
||||||
|
authorizeApplication(
|
||||||
|
clientId: $applicationClientId
|
||||||
|
redirectURI: $redirectURI
|
||||||
|
state: $state
|
||||||
|
scope: $scope
|
||||||
|
) {
|
||||||
|
code
|
||||||
|
state
|
||||||
|
clientId
|
||||||
|
scope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
test "while being not logged-in", %{conn: conn} do
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @authorize_mutation,
|
||||||
|
variables: [
|
||||||
|
applicationClientId: "an invalid client_id",
|
||||||
|
redirectURI: "doesn't matter",
|
||||||
|
state: "hello",
|
||||||
|
scope: "read"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "You need to be logged in" = hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with incorrect client_id", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @authorize_mutation,
|
||||||
|
variables: [
|
||||||
|
applicationClientId: "an invalid client_id",
|
||||||
|
redirectURI: "doesn't matter",
|
||||||
|
state: "hello",
|
||||||
|
scope: "read"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "No application with this client_id was found" = hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with incorrect redirect_uri", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
app = insert(:auth_application)
|
||||||
|
|
||||||
|
client_id = app.client_id
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @authorize_mutation,
|
||||||
|
variables: [
|
||||||
|
applicationClientId: client_id,
|
||||||
|
redirectURI: "something not in app's redirect URIs",
|
||||||
|
state: "hello",
|
||||||
|
scope: "read"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "The given redirect_uri is not in the list of allowed redirect URIs" =
|
||||||
|
hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with correct params", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
app = insert(:auth_application)
|
||||||
|
|
||||||
|
client_id = app.client_id
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @authorize_mutation,
|
||||||
|
variables: [
|
||||||
|
applicationClientId: client_id,
|
||||||
|
redirectURI: hd(app.redirect_uris),
|
||||||
|
state: "hello",
|
||||||
|
scope: "read"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"scope" => "read",
|
||||||
|
"state" => "hello",
|
||||||
|
"clientId" => ^client_id,
|
||||||
|
"code" => _code
|
||||||
|
} = res["data"]["authorizeApplication"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Revoke an application token" do
|
||||||
|
@revoke_mutation """
|
||||||
|
mutation RevokeApplicationToken($appTokenId: String!) {
|
||||||
|
revokeApplicationToken(appTokenId: $appTokenId) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
test "while not authenticated", %{conn: conn} do
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @revoke_mutation,
|
||||||
|
variables: [
|
||||||
|
appTokenId: "not an actual token ID"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "You need to be logged in" = hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with an invalid token", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @revoke_mutation,
|
||||||
|
variables: [
|
||||||
|
appTokenId: "5846"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "Application token not found" == hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with valid token", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
app_token = insert(:auth_application_token, user: user)
|
||||||
|
app_token_id = to_string(app_token.id)
|
||||||
|
|
||||||
|
authed_conn = auth_conn(conn, app_token)
|
||||||
|
|
||||||
|
res = AbsintheHelpers.graphql_query(authed_conn, query: @identities_query)
|
||||||
|
assert res["errors"] == nil
|
||||||
|
assert res["data"]["loggedUser"]["actors"]
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @revoke_mutation,
|
||||||
|
variables: [
|
||||||
|
appTokenId: app_token_id
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert app_token_id == res["data"]["revokeApplicationToken"]["id"]
|
||||||
|
|
||||||
|
# Asserting the token can't be used anymore
|
||||||
|
res = AbsintheHelpers.graphql_query(authed_conn, query: @identities_query)
|
||||||
|
assert "You need to be logged in" == hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Get an application" do
|
||||||
|
@application_query """
|
||||||
|
query AuthApplication($clientId: String!) {
|
||||||
|
authApplication(clientId: $clientId) {
|
||||||
|
id
|
||||||
|
clientId
|
||||||
|
name
|
||||||
|
website
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
test "while not authenticated", %{conn: conn} do
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @application_query,
|
||||||
|
variables: [
|
||||||
|
clientId: "not an actual client ID"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "You need to be logged in" = hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with incorrect client_id", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @application_query,
|
||||||
|
variables: [
|
||||||
|
clientId: "nonsense"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "Application not found" = hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with valid client_id", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
%Application{id: app_id, client_id: app_client_id, name: app_name, website: app_website} =
|
||||||
|
insert(:auth_application)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @application_query,
|
||||||
|
variables: [
|
||||||
|
clientId: app_client_id
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert is_nil(res["errors"])
|
||||||
|
|
||||||
|
app_id = to_string(app_id)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"id" => ^app_id,
|
||||||
|
"clientId" => ^app_client_id,
|
||||||
|
"name" => ^app_name,
|
||||||
|
"website" => ^app_website
|
||||||
|
} = res["data"]["authApplication"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Get user applications" do
|
||||||
|
@user_apps_query """
|
||||||
|
query AuthAuthorizedApplications {
|
||||||
|
loggedUser {
|
||||||
|
id
|
||||||
|
authAuthorizedApplications {
|
||||||
|
id
|
||||||
|
application {
|
||||||
|
name
|
||||||
|
website
|
||||||
|
}
|
||||||
|
lastUsedAt
|
||||||
|
insertedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
test "without being logged in", %{conn: conn} do
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> AbsintheHelpers.graphql_query(query: @user_apps_query)
|
||||||
|
|
||||||
|
assert "You need to be logged in" = hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with an app token", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
app_token = insert(:auth_application_token, user: user)
|
||||||
|
|
||||||
|
insert(:auth_application_token, user: user, status: :success, authorization_code: nil)
|
||||||
|
|
||||||
|
insert(:auth_application_token, user: user, status: :success, authorization_code: nil)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(app_token)
|
||||||
|
|> AbsintheHelpers.graphql_query(query: @user_apps_query)
|
||||||
|
|
||||||
|
assert is_nil(res["data"]["loggedUser"]["authAuthorizedApplications"])
|
||||||
|
refute is_nil(res["data"]["loggedUser"]["id"])
|
||||||
|
assert hd(res["errors"])["message"] =~ "Not authorized to access field"
|
||||||
|
assert hd(res["errors"])["path"] == ["loggedUser", "authAuthorizedApplications"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with authorized applications", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
app_token_1 =
|
||||||
|
insert(:auth_application_token, user: user, status: :success, authorization_code: nil)
|
||||||
|
|
||||||
|
app_token_2 =
|
||||||
|
insert(:auth_application_token, user: user, status: :success, authorization_code: nil)
|
||||||
|
|
||||||
|
# Someone else's app token
|
||||||
|
app_token_3 = insert(:auth_application_token, status: :success, authorization_code: nil)
|
||||||
|
# An app token not activated
|
||||||
|
app_token_4 = insert(:auth_application_token, user: user)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(query: @user_apps_query)
|
||||||
|
|
||||||
|
assert is_nil(res["errors"])
|
||||||
|
assert 2 = length(res["data"]["loggedUser"]["authAuthorizedApplications"])
|
||||||
|
|
||||||
|
found_app_token_ids =
|
||||||
|
res["data"]["loggedUser"]["authAuthorizedApplications"]
|
||||||
|
|> Enum.map(&String.to_integer(&1["id"]))
|
||||||
|
|> MapSet.new()
|
||||||
|
|
||||||
|
assert MapSet.subset?(MapSet.new([app_token_1.id, app_token_2.id]), found_app_token_ids)
|
||||||
|
refute MapSet.member?(found_app_token_ids, app_token_3.id)
|
||||||
|
refute MapSet.member?(found_app_token_ids, app_token_4.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Device activation" do
|
||||||
|
@device_activation_mutation """
|
||||||
|
mutation DeviceActivation($userCode: String!) {
|
||||||
|
deviceActivation(userCode: $userCode) {
|
||||||
|
id
|
||||||
|
application {
|
||||||
|
id
|
||||||
|
clientId
|
||||||
|
name
|
||||||
|
website
|
||||||
|
}
|
||||||
|
scope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
test "without being logged-in", %{conn: conn} do
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @device_activation_mutation,
|
||||||
|
variables: [userCode: "hi"]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "You need to be logged in" = hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with a bad code", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @device_activation_mutation,
|
||||||
|
variables: [userCode: "hi"]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "The given user code is invalid" = hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with an expired code", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
auth_application_device_activation =
|
||||||
|
insert(:auth_application_device_activation, user: user, expires_in: -100)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @device_activation_mutation,
|
||||||
|
variables: [userCode: auth_application_device_activation.user_code]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "The given user code has expired" = hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with a valid code", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
auth_application_device_activation = insert(:auth_application_device_activation, user: nil)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @device_activation_mutation,
|
||||||
|
variables: [userCode: auth_application_device_activation.user_code]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert is_nil(res["errors"])
|
||||||
|
|
||||||
|
assert res["data"]["deviceActivation"]["application"]["id"] ==
|
||||||
|
to_string(auth_application_device_activation.application.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Device authorization" do
|
||||||
|
@device_authorization_mutation """
|
||||||
|
mutation AuthorizeDeviceApplication(
|
||||||
|
$applicationClientId: String!
|
||||||
|
$userCode: String!
|
||||||
|
) {
|
||||||
|
authorizeDeviceApplication(
|
||||||
|
clientId: $applicationClientId
|
||||||
|
userCode: $userCode
|
||||||
|
) {
|
||||||
|
clientId
|
||||||
|
scope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
test "without being logged in", %{conn: conn} do
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @device_authorization_mutation,
|
||||||
|
variables: [applicationClientId: "something", userCode: "wrong"]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "You need to be logged in" = hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with a bad code", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @device_authorization_mutation,
|
||||||
|
variables: [applicationClientId: "something", userCode: "wrong"]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "The given user code is invalid" = hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with some code that isn't approved", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
auth_application_device_activation =
|
||||||
|
insert(:auth_application_device_activation, user: user, status: :pending)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @device_authorization_mutation,
|
||||||
|
variables: [
|
||||||
|
applicationClientId: auth_application_device_activation.application.client_id,
|
||||||
|
userCode: auth_application_device_activation.user_code
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "The device user code was not provided before approving the application" =
|
||||||
|
hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with some expired code", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
auth_application_device_activation =
|
||||||
|
insert(:auth_application_device_activation,
|
||||||
|
user: user,
|
||||||
|
status: :confirmed,
|
||||||
|
expires_in: -100
|
||||||
|
)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @device_authorization_mutation,
|
||||||
|
variables: [
|
||||||
|
applicationClientId: auth_application_device_activation.application.client_id,
|
||||||
|
userCode: auth_application_device_activation.user_code
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "The given user code has expired" = hd(res["errors"])["message"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with a valid code", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
%ApplicationDeviceActivation{
|
||||||
|
application: %Application{client_id: client_id},
|
||||||
|
user_code: user_code
|
||||||
|
} = insert(:auth_application_device_activation, user: user, status: :confirmed)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @device_authorization_mutation,
|
||||||
|
variables: [
|
||||||
|
applicationClientId: client_id,
|
||||||
|
userCode: user_code
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert is_nil(res["errors"])
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"clientId" => ^client_id,
|
||||||
|
"scope" => _scope
|
||||||
|
} = res["data"]["authorizeDeviceApplication"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -99,7 +99,7 @@ defmodule Mobilizon.GraphQL.Resolvers.CommentTest do
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] ==
|
assert hd(res["errors"])["message"] ==
|
||||||
"You are not allowed to create a comment if not connected"
|
"You need to be logged in"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "create_comment/3 creates a reply to a comment", %{
|
test "create_comment/3 creates a reply to a comment", %{
|
||||||
|
@ -166,7 +166,7 @@ defmodule Mobilizon.GraphQL.Resolvers.CommentTest do
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] ==
|
assert hd(res["errors"])["message"] ==
|
||||||
"You are not allowed to delete a comment if not connected"
|
"You need to be logged in"
|
||||||
|
|
||||||
# Change the current actor for user
|
# Change the current actor for user
|
||||||
actor2 = insert(:actor, user: user)
|
actor2 = insert(:actor, user: user)
|
||||||
|
@ -218,10 +218,11 @@ defmodule Mobilizon.GraphQL.Resolvers.CommentTest do
|
||||||
variables: %{commentId: comment.id}
|
variables: %{commentId: comment.id}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert res["errors"] == nil
|
||||||
assert res["data"]["deleteComment"]["id"] == to_string(comment.id)
|
assert res["data"]["deleteComment"]["id"] == to_string(comment.id)
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
{
|
query ActionLogs {
|
||||||
actionLogs {
|
actionLogs {
|
||||||
total
|
total
|
||||||
elements {
|
elements {
|
||||||
|
@ -254,11 +255,11 @@ defmodule Mobilizon.GraphQL.Resolvers.CommentTest do
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> auth_conn(user_moderator)
|
|> auth_conn(user_moderator)
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "actionLogs"))
|
|> AbsintheHelpers.graphql_query(query: query)
|
||||||
|
|
||||||
refute json_response(res, 200)["errors"]
|
refute res["errors"]
|
||||||
|
|
||||||
assert hd(json_response(res, 200)["data"]["actionLogs"]["elements"]) == %{
|
assert hd(res["data"]["actionLogs"]["elements"]) == %{
|
||||||
"action" => "COMMENT_DELETION",
|
"action" => "COMMENT_DELETION",
|
||||||
"actor" => %{"preferredUsername" => actor_moderator.preferred_username},
|
"actor" => %{"preferredUsername" => actor_moderator.preferred_username},
|
||||||
"object" => %{"text" => comment.text, "id" => to_string(comment.id)}
|
"object" => %{"text" => comment.text, "id" => to_string(comment.id)}
|
||||||
|
|
|
@ -1510,53 +1510,51 @@ defmodule Mobilizon.Web.Resolvers.EventTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "delete_event/3" do
|
describe "delete_event/3" do
|
||||||
test "delete_event/3 deletes an event", %{conn: conn, user: user, actor: actor} do
|
@delete_event_mutation """
|
||||||
event = insert(:event, organizer_actor: actor)
|
mutation DeleteEvent($eventId: ID!) {
|
||||||
|
|
||||||
mutation = """
|
|
||||||
mutation {
|
|
||||||
deleteEvent(
|
deleteEvent(
|
||||||
event_id: #{event.id}
|
eventId: $eventId
|
||||||
) {
|
) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
res =
|
test "delete_event/3 deletes an event", %{conn: conn, user: user, actor: actor} do
|
||||||
conn
|
event = insert(:event, organizer_actor: actor)
|
||||||
|> auth_conn(user)
|
|
||||||
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
|
||||||
|
|
||||||
assert json_response(res, 200)["errors"] == nil
|
|
||||||
assert json_response(res, 200)["data"]["deleteEvent"]["id"] == to_string(event.id)
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> auth_conn(user)
|
|> auth_conn(user)
|
||||||
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @delete_event_mutation,
|
||||||
|
variables: [eventId: event.id]
|
||||||
|
)
|
||||||
|
|
||||||
assert hd(json_response(res, 200)["errors"])["message"] =~ "not found"
|
assert res["errors"] == nil
|
||||||
|
assert res["data"]["deleteEvent"]["id"] == to_string(event.id)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @delete_event_mutation,
|
||||||
|
variables: [eventId: event.id]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert hd(res["errors"])["message"] =~ "not found"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "delete_event/3 should check the user is authenticated", %{conn: conn, actor: actor} do
|
test "delete_event/3 should check the user is authenticated", %{conn: conn, actor: actor} do
|
||||||
event = insert(:event, organizer_actor: actor)
|
event = insert(:event, organizer_actor: actor)
|
||||||
|
|
||||||
mutation = """
|
|
||||||
mutation {
|
|
||||||
deleteEvent(
|
|
||||||
event_id: #{event.id}
|
|
||||||
) {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
AbsintheHelpers.graphql_query(conn,
|
||||||
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
query: @delete_event_mutation,
|
||||||
|
variables: [eventId: event.id]
|
||||||
|
)
|
||||||
|
|
||||||
assert hd(json_response(res, 200)["errors"])["message"] =~ "logged-in"
|
assert hd(res["errors"])["message"] =~ "logged in"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "delete_event/3 should check the event can be deleted by the user", %{
|
test "delete_event/3 should check the event can be deleted by the user", %{
|
||||||
|
@ -1567,22 +1565,15 @@ defmodule Mobilizon.Web.Resolvers.EventTest do
|
||||||
actor2 = insert(:actor)
|
actor2 = insert(:actor)
|
||||||
event = insert(:event, organizer_actor: actor2)
|
event = insert(:event, organizer_actor: actor2)
|
||||||
|
|
||||||
mutation = """
|
|
||||||
mutation {
|
|
||||||
deleteEvent(
|
|
||||||
event_id: #{event.id}
|
|
||||||
) {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> auth_conn(user)
|
|> auth_conn(user)
|
||||||
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @delete_event_mutation,
|
||||||
|
variables: [eventId: event.id]
|
||||||
|
)
|
||||||
|
|
||||||
assert hd(json_response(res, 200)["errors"])["message"] =~ "cannot delete"
|
assert hd(res["errors"])["message"] =~ "cannot delete"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "delete_event/3 allows a event being deleted by a moderator and creates a entry in actionLogs",
|
test "delete_event/3 allows a event being deleted by a moderator and creates a entry in actionLogs",
|
||||||
|
@ -1597,22 +1588,16 @@ defmodule Mobilizon.Web.Resolvers.EventTest do
|
||||||
actor2 = insert(:actor)
|
actor2 = insert(:actor)
|
||||||
event = insert(:event, organizer_actor: actor2)
|
event = insert(:event, organizer_actor: actor2)
|
||||||
|
|
||||||
mutation = """
|
|
||||||
mutation {
|
|
||||||
deleteEvent(
|
|
||||||
event_id: #{event.id}
|
|
||||||
) {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> auth_conn(user_moderator)
|
|> auth_conn(user_moderator)
|
||||||
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @delete_event_mutation,
|
||||||
|
variables: [eventId: event.id]
|
||||||
|
)
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["deleteEvent"]["id"] == to_string(event.id)
|
assert res["errors"] == nil
|
||||||
|
assert res["data"]["deleteEvent"]["id"] == to_string(event.id)
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,44 +13,8 @@ defmodule Mobilizon.GraphQL.Resolvers.FeedTokenTest do
|
||||||
{:ok, conn: conn, actor: actor, user: user}
|
{:ok, conn: conn, actor: actor, user: user}
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "Feed Token Resolver" do
|
@user_feed_tokens_query """
|
||||||
test "create_feed_token/3 should create a feed token", %{conn: conn, user: user} do
|
query LoggedUserFeedTokens {
|
||||||
actor2 = insert(:actor, user: user)
|
|
||||||
|
|
||||||
mutation = """
|
|
||||||
mutation {
|
|
||||||
createFeedToken(
|
|
||||||
actor_id: #{actor2.id},
|
|
||||||
) {
|
|
||||||
token,
|
|
||||||
actor {
|
|
||||||
id
|
|
||||||
},
|
|
||||||
user {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
|
||||||
conn
|
|
||||||
|> auth_conn(user)
|
|
||||||
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
|
||||||
|
|
||||||
assert json_response(res, 200)["errors"] == nil
|
|
||||||
token = json_response(res, 200)["data"]["createFeedToken"]["token"]
|
|
||||||
assert is_binary(token)
|
|
||||||
# TODO: Investigate why user id is a string when actor id is a number
|
|
||||||
assert json_response(res, 200)["data"]["createFeedToken"]["user"]["id"] ==
|
|
||||||
to_string(user.id)
|
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["createFeedToken"]["actor"]["id"] ==
|
|
||||||
to_string(actor2.id)
|
|
||||||
|
|
||||||
# The token is present for the user
|
|
||||||
query = """
|
|
||||||
{
|
|
||||||
loggedUser {
|
loggedUser {
|
||||||
feedTokens {
|
feedTokens {
|
||||||
token
|
token
|
||||||
|
@ -59,19 +23,8 @@ defmodule Mobilizon.GraphQL.Resolvers.FeedTokenTest do
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
res =
|
@logged_person_feed_tokens_query """
|
||||||
conn
|
query LoggedPersonFeedTokens {
|
||||||
|> auth_conn(user)
|
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "loggedUser"))
|
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["loggedUser"] ==
|
|
||||||
%{
|
|
||||||
"feedTokens" => [%{"token" => token}]
|
|
||||||
}
|
|
||||||
|
|
||||||
# But not for this identity
|
|
||||||
query = """
|
|
||||||
{
|
|
||||||
loggedPerson {
|
loggedPerson {
|
||||||
feedTokens {
|
feedTokens {
|
||||||
token
|
token
|
||||||
|
@ -80,20 +33,14 @@ defmodule Mobilizon.GraphQL.Resolvers.FeedTokenTest do
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
res =
|
describe "Feed Token Resolver" do
|
||||||
conn
|
@create_feed_token_for_actor_mutation """
|
||||||
|> auth_conn(user)
|
mutation CreatePersonFeedToken($actorId: ID!) {
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "loggedPerson"))
|
createFeedToken(actorId: $actorId) {
|
||||||
|
token
|
||||||
assert json_response(res, 200)["data"]["loggedPerson"] ==
|
actor {
|
||||||
%{
|
id
|
||||||
"feedTokens" => []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mutation = """
|
|
||||||
mutation {
|
|
||||||
createFeedToken {
|
|
||||||
token,
|
|
||||||
user {
|
user {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
@ -101,36 +48,80 @@ defmodule Mobilizon.GraphQL.Resolvers.FeedTokenTest do
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
res =
|
@create_feed_token_for_user_mutation """
|
||||||
conn
|
mutation CreateFeedToken {
|
||||||
|> auth_conn(user)
|
createFeedToken {
|
||||||
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
|
||||||
|
|
||||||
assert json_response(res, 200)["errors"] == nil
|
|
||||||
token2 = json_response(res, 200)["data"]["createFeedToken"]["token"]
|
|
||||||
assert is_binary(token2)
|
|
||||||
assert is_nil(json_response(res, 200)["data"]["createFeedToken"]["actor"])
|
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["createFeedToken"]["user"]["id"] ==
|
|
||||||
to_string(user.id)
|
|
||||||
|
|
||||||
# The token is present for the user
|
|
||||||
query = """
|
|
||||||
{
|
|
||||||
loggedUser {
|
|
||||||
feedTokens {
|
|
||||||
token
|
token
|
||||||
|
user {
|
||||||
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
test "create_feed_token/3 should create a feed token", %{conn: conn, user: user} do
|
||||||
|
actor2 = insert(:actor, user: user)
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> auth_conn(user)
|
|> auth_conn(user)
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "loggedUser"))
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @create_feed_token_for_actor_mutation,
|
||||||
|
variables: [actorId: actor2.id]
|
||||||
|
)
|
||||||
|
|
||||||
assert json_response(res, 200)["data"]["loggedUser"] ==
|
assert res["errors"] == nil
|
||||||
|
token = res["data"]["createFeedToken"]["token"]
|
||||||
|
assert is_binary(token)
|
||||||
|
|
||||||
|
assert res["data"]["createFeedToken"]["user"]["id"] ==
|
||||||
|
to_string(user.id)
|
||||||
|
|
||||||
|
assert res["data"]["createFeedToken"]["actor"]["id"] ==
|
||||||
|
to_string(actor2.id)
|
||||||
|
|
||||||
|
# The token is present for the user
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(query: @user_feed_tokens_query)
|
||||||
|
|
||||||
|
assert res["data"]["loggedUser"] ==
|
||||||
|
%{
|
||||||
|
"feedTokens" => [%{"token" => token}]
|
||||||
|
}
|
||||||
|
|
||||||
|
# But not for this identity
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(query: @logged_person_feed_tokens_query)
|
||||||
|
|
||||||
|
assert res["data"]["loggedPerson"] ==
|
||||||
|
%{
|
||||||
|
"feedTokens" => []
|
||||||
|
}
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(query: @create_feed_token_for_user_mutation)
|
||||||
|
|
||||||
|
assert res["errors"] == nil
|
||||||
|
token2 = res["data"]["createFeedToken"]["token"]
|
||||||
|
assert is_binary(token2)
|
||||||
|
assert is_nil(res["data"]["createFeedToken"]["actor"])
|
||||||
|
|
||||||
|
assert res["data"]["createFeedToken"]["user"]["id"] ==
|
||||||
|
to_string(user.id)
|
||||||
|
|
||||||
|
# The token is present for the user
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(query: @user_feed_tokens_query)
|
||||||
|
|
||||||
|
assert res["data"]["loggedUser"] ==
|
||||||
%{
|
%{
|
||||||
"feedTokens" => [%{"token" => token}, %{"token" => token2}]
|
"feedTokens" => [%{"token" => token}, %{"token" => token2}]
|
||||||
}
|
}
|
||||||
|
@ -142,22 +133,15 @@ defmodule Mobilizon.GraphQL.Resolvers.FeedTokenTest do
|
||||||
} do
|
} do
|
||||||
actor = insert(:actor)
|
actor = insert(:actor)
|
||||||
|
|
||||||
mutation = """
|
|
||||||
mutation {
|
|
||||||
createFeedToken(
|
|
||||||
actor_id: #{actor.id}
|
|
||||||
) {
|
|
||||||
token
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> auth_conn(user)
|
|> auth_conn(user)
|
||||||
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @create_feed_token_for_actor_mutation,
|
||||||
|
variables: [actorId: actor.id]
|
||||||
|
)
|
||||||
|
|
||||||
assert hd(json_response(res, 200)["errors"])["message"] =~ "not owned"
|
assert hd(res["errors"])["message"] =~ "not owned"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "delete_feed_token/3 should delete a feed token", %{
|
test "delete_feed_token/3 should delete a feed token", %{
|
||||||
|
@ -257,7 +241,7 @@ defmodule Mobilizon.GraphQL.Resolvers.FeedTokenTest do
|
||||||
conn
|
conn
|
||||||
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
||||||
|
|
||||||
assert hd(json_response(res, 200)["errors"])["message"] =~ "if not connected"
|
assert "You need to be logged in" == hd(json_response(res, 200)["errors"])["message"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "delete_feed_token/3 should check the correct user is logged in", %{
|
test "delete_feed_token/3 should check the correct user is logged in", %{
|
||||||
|
|
|
@ -70,9 +70,8 @@ defmodule Mobilizon.Web.Resolvers.FollowerTest do
|
||||||
variables: %{name: preferred_username}
|
variables: %{name: preferred_username}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert res["errors"] == nil
|
assert hd(res["errors"])["message"] ==
|
||||||
assert res["data"]["group"]["followers"]["total"] == 1
|
"Not authorized to access object paginated_follower_list"
|
||||||
assert res["data"]["group"]["followers"]["elements"] == []
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "without being a member", %{
|
test "without being a member", %{
|
||||||
|
|
|
@ -107,7 +107,7 @@ defmodule Mobilizon.Web.Resolvers.GroupTest do
|
||||||
|
|
||||||
res = AbsintheHelpers.graphql_query(conn, query: @list_groups_query)
|
res = AbsintheHelpers.graphql_query(conn, query: @list_groups_query)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "You may not list groups unless moderator."
|
assert hd(res["errors"])["message"] == "You need to be logged in"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "list_groups/3 doesn't return all groups if not a moderator", %{conn: conn} do
|
test "list_groups/3 doesn't return all groups if not a moderator", %{conn: conn} do
|
||||||
|
@ -121,7 +121,7 @@ defmodule Mobilizon.Web.Resolvers.GroupTest do
|
||||||
|> auth_conn(user)
|
|> auth_conn(user)
|
||||||
|> AbsintheHelpers.graphql_query(query: @list_groups_query)
|
|> AbsintheHelpers.graphql_query(query: @list_groups_query)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "You may not list groups unless moderator."
|
assert hd(res["errors"])["message"] == "You don't have permission to do this"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "list_groups/3 returns all groups if a moderator", %{conn: conn} do
|
test "list_groups/3 returns all groups if a moderator", %{conn: conn} do
|
||||||
|
@ -146,6 +146,14 @@ defmodule Mobilizon.Web.Resolvers.GroupTest do
|
||||||
|
|
||||||
describe "find a group" do
|
describe "find a group" do
|
||||||
@group_query """
|
@group_query """
|
||||||
|
query Group($preferredUsername: String!) {
|
||||||
|
group(preferredUsername: $preferredUsername) {
|
||||||
|
preferredUsername
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
@group_with_member_query """
|
||||||
query Group($preferredUsername: String!) {
|
query Group($preferredUsername: String!) {
|
||||||
group(preferredUsername: $preferredUsername) {
|
group(preferredUsername: $preferredUsername) {
|
||||||
preferredUsername,
|
preferredUsername,
|
||||||
|
@ -173,19 +181,14 @@ defmodule Mobilizon.Web.Resolvers.GroupTest do
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> AbsintheHelpers.graphql_query(
|
|> AbsintheHelpers.graphql_query(
|
||||||
query: @group_query,
|
query: @group_with_member_query,
|
||||||
variables: %{
|
variables: %{
|
||||||
preferredUsername: group.preferred_username
|
preferredUsername: group.preferred_username
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert res["errors"] == nil
|
assert hd(res["errors"])["message"] ==
|
||||||
|
"Not authorized to access object paginated_member_list"
|
||||||
assert res["data"]["group"]["preferredUsername"] ==
|
|
||||||
group.preferred_username
|
|
||||||
|
|
||||||
assert res["data"]["group"]["members"]["total"] == 2
|
|
||||||
assert res["data"]["group"]["members"]["elements"] == []
|
|
||||||
|
|
||||||
# Login with non-member
|
# Login with non-member
|
||||||
res =
|
res =
|
||||||
|
@ -203,15 +206,12 @@ defmodule Mobilizon.Web.Resolvers.GroupTest do
|
||||||
assert res["data"]["group"]["preferredUsername"] ==
|
assert res["data"]["group"]["preferredUsername"] ==
|
||||||
group.preferred_username
|
group.preferred_username
|
||||||
|
|
||||||
assert res["data"]["group"]["members"]["total"] == 2
|
|
||||||
assert res["data"]["group"]["members"]["elements"] == []
|
|
||||||
|
|
||||||
# Login with member
|
# Login with member
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> auth_conn(user)
|
|> auth_conn(user)
|
||||||
|> AbsintheHelpers.graphql_query(
|
|> AbsintheHelpers.graphql_query(
|
||||||
query: @group_query,
|
query: @group_with_member_query,
|
||||||
variables: %{
|
variables: %{
|
||||||
preferredUsername: group.preferred_username,
|
preferredUsername: group.preferred_username,
|
||||||
actorId: actor.id
|
actorId: actor.id
|
||||||
|
@ -252,18 +252,14 @@ defmodule Mobilizon.Web.Resolvers.GroupTest do
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> AbsintheHelpers.graphql_query(
|
|> AbsintheHelpers.graphql_query(
|
||||||
query: @group_query,
|
query: @group_with_member_query,
|
||||||
variables: %{
|
variables: %{
|
||||||
preferredUsername: group.preferred_username
|
preferredUsername: group.preferred_username
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert res["errors"] == nil
|
assert hd(res["errors"])["message"] ==
|
||||||
|
"Not authorized to access object paginated_member_list"
|
||||||
assert res["data"]["group"]["preferredUsername"] ==
|
|
||||||
group.preferred_username
|
|
||||||
|
|
||||||
assert res["data"]["group"]["members"] == %{"elements" => [], "total" => 1}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -334,7 +330,7 @@ defmodule Mobilizon.Web.Resolvers.GroupTest do
|
||||||
variables: %{id: group.id, name: @new_group_name}
|
variables: %{id: group.id, name: @new_group_name}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "You need to be logged-in to update a group"
|
assert hd(res["errors"])["message"] == "You need to be logged in"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "update_group/3 requires to be an admin of the group to update a group", %{
|
test "update_group/3 requires to be an admin of the group to update a group", %{
|
||||||
|
@ -436,7 +432,7 @@ defmodule Mobilizon.Web.Resolvers.GroupTest do
|
||||||
variables: %{groupId: group.id}
|
variables: %{groupId: group.id}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] =~ "logged-in"
|
assert hd(res["errors"])["message"] == "You need to be logged in"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "delete_group/3 should check the actor is owned by the user", %{
|
test "delete_group/3 should check the actor is owned by the user", %{
|
||||||
|
@ -515,7 +511,7 @@ defmodule Mobilizon.Web.Resolvers.GroupTest do
|
||||||
variables: %{groupId: group.id}
|
variables: %{groupId: group.id}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "You need to be logged-in to follow a group"
|
assert hd(res["errors"])["message"] == "You need to be logged in"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "when group doesn't exist", %{conn: conn, user: user} do
|
test "when group doesn't exist", %{conn: conn, user: user} do
|
||||||
|
@ -564,7 +560,7 @@ defmodule Mobilizon.Web.Resolvers.GroupTest do
|
||||||
variables: %{groupId: group.id}
|
variables: %{groupId: group.id}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "You need to be logged-in to unfollow a group"
|
assert hd(res["errors"])["message"] == "You need to be logged in"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "when group doesn't exist", %{conn: conn, user: user} do
|
test "when group doesn't exist", %{conn: conn, user: user} do
|
||||||
|
@ -631,7 +627,7 @@ defmodule Mobilizon.Web.Resolvers.GroupTest do
|
||||||
variables: %{followId: follow.id}
|
variables: %{followId: follow.id}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "You need to be logged-in to update a group follow"
|
assert hd(res["errors"])["message"] == "You need to be logged in"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "when follow doesn't exist", %{conn: conn, user: user} do
|
test "when follow doesn't exist", %{conn: conn, user: user} do
|
||||||
|
|
|
@ -434,7 +434,7 @@ defmodule Mobilizon.GraphQL.Resolvers.MediaTest do
|
||||||
variables: %{email: user.email}
|
variables: %{email: user.email}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert is_nil(res["errors"])
|
assert res["errors"] == nil
|
||||||
assert hd(res["data"]["users"]["elements"])["mediaSize"] == 0
|
assert hd(res["data"]["users"]["elements"])["mediaSize"] == 0
|
||||||
|
|
||||||
res = upload_media(conn, user)
|
res = upload_media(conn, user)
|
||||||
|
|
|
@ -152,7 +152,7 @@ defmodule Mobilizon.GraphQL.Resolvers.MemberTest do
|
||||||
variables: %{groupId: group.id}
|
variables: %{groupId: group.id}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] =~ "logged-in"
|
assert hd(res["errors"])["message"] == "You need to be logged in"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "leave_group/3 should check the group exists", %{
|
test "leave_group/3 should check the group exists", %{
|
||||||
|
@ -432,7 +432,7 @@ defmodule Mobilizon.GraphQL.Resolvers.MemberTest do
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "You must be logged-in to update a member"
|
assert hd(res["errors"])["message"] == "You need to be logged in"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "update_member/3 fails when not a member of the group", %{
|
test "update_member/3 fails when not a member of the group", %{
|
||||||
|
@ -575,7 +575,7 @@ defmodule Mobilizon.GraphQL.Resolvers.MemberTest do
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "You must be logged-in to remove a member"
|
assert hd(res["errors"])["message"] == "You need to be logged in"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "remove_member/3 fails when not a member of the group", %{
|
test "remove_member/3 fails when not a member of the group", %{
|
||||||
|
|
|
@ -29,6 +29,7 @@ defmodule Mobilizon.GraphQL.Resolvers.PersonTest do
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# TODO: Remove this
|
||||||
@fetch_identities_query """
|
@fetch_identities_query """
|
||||||
{
|
{
|
||||||
identities {
|
identities {
|
||||||
|
@ -824,7 +825,7 @@ defmodule Mobilizon.GraphQL.Resolvers.PersonTest do
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] ==
|
assert hd(res["errors"])["message"] ==
|
||||||
"Only moderators and administrators can suspend a profile"
|
"You don't have permission to do this"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -165,7 +165,7 @@ defmodule Mobilizon.GraphQL.Resolvers.ReportTest do
|
||||||
)
|
)
|
||||||
|
|
||||||
assert res["errors"] |> hd |> Map.get("message") ==
|
assert res["errors"] |> hd |> Map.get("message") ==
|
||||||
"You need to be logged-in and a moderator to update a report"
|
"You need to be logged in"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "update_report/3 without being a moderator doesn't update any report", %{conn: conn} do
|
test "update_report/3 without being a moderator doesn't update any report", %{conn: conn} do
|
||||||
|
@ -181,7 +181,7 @@ defmodule Mobilizon.GraphQL.Resolvers.ReportTest do
|
||||||
)
|
)
|
||||||
|
|
||||||
assert res["errors"] |> hd |> Map.get("message") ==
|
assert res["errors"] |> hd |> Map.get("message") ==
|
||||||
"You need to be logged-in and a moderator to update a report"
|
"You don't have permission to do this"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ defmodule Mobilizon.GraphQL.Resolvers.ReportTest do
|
||||||
res = AbsintheHelpers.graphql_query(conn, query: @reports_query)
|
res = AbsintheHelpers.graphql_query(conn, query: @reports_query)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] ==
|
assert hd(res["errors"])["message"] ==
|
||||||
"You need to be logged-in and a moderator to list reports"
|
"You need to be logged in"
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|
@ -296,7 +296,7 @@ defmodule Mobilizon.GraphQL.Resolvers.ReportTest do
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "report"))
|
|> get("/api", AbsintheHelpers.query_skeleton(query, "report"))
|
||||||
|
|
||||||
assert json_response(res, 200)["errors"] |> hd |> Map.get("message") ==
|
assert json_response(res, 200)["errors"] |> hd |> Map.get("message") ==
|
||||||
"You need to be logged-in and a moderator to view a report"
|
"You need to be logged in"
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -260,10 +260,8 @@ defmodule Mobilizon.GraphQL.Resolvers.ResourceTest do
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert is_nil(res["errors"])
|
assert hd(res["errors"])["message"] ==
|
||||||
|
"Not authorized to access object paginated_resource_list"
|
||||||
assert res["data"]["group"]["resources"]["total"] == 0
|
|
||||||
assert res["data"]["group"]["resources"]["elements"] == []
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -380,7 +378,7 @@ defmodule Mobilizon.GraphQL.Resolvers.ResourceTest do
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] == "You need to be logged-in to access resources"
|
assert hd(res["errors"])["message"] == "You need to be logged in"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -239,21 +239,44 @@ defmodule Mobilizon.GraphQL.Resolvers.SearchTest do
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
test "finds persons with basic search", %{
|
test "without being logged-in", %{
|
||||||
conn: conn,
|
conn: conn
|
||||||
user: user
|
|
||||||
} do
|
} do
|
||||||
actor = insert(:actor, user: user, preferred_username: "test_person")
|
|
||||||
insert(:actor, type: :Group, preferred_username: "test_group")
|
|
||||||
event = insert(:event, title: "test_event")
|
|
||||||
Workers.BuildSearch.insert_search_event(event)
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
AbsintheHelpers.graphql_query(conn,
|
AbsintheHelpers.graphql_query(conn,
|
||||||
query: @search_persons_query,
|
query: @search_persons_query,
|
||||||
variables: %{term: "test"}
|
variables: %{term: "test"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert hd(res["errors"])["message"] == "You need to be logged in"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "without being a moderator", %{
|
||||||
|
conn: conn,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(query: @search_persons_query, variables: %{term: "test"})
|
||||||
|
|
||||||
|
assert hd(res["errors"])["message"] == "You don't have permission to do this"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "finds persons with basic search", %{
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
user = insert(:user, role: :moderator)
|
||||||
|
actor = insert(:actor, preferred_username: "test_person")
|
||||||
|
insert(:actor, type: :Group, preferred_username: "test_group")
|
||||||
|
event = insert(:event, title: "test_event")
|
||||||
|
Workers.BuildSearch.insert_search_event(event)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(query: @search_persons_query, variables: %{term: "test"})
|
||||||
|
|
||||||
assert res["errors"] == nil
|
assert res["errors"] == nil
|
||||||
assert res["data"]["searchPersons"]["total"] == 1
|
assert res["data"]["searchPersons"]["total"] == 1
|
||||||
assert res["data"]["searchPersons"]["elements"] |> length == 1
|
assert res["data"]["searchPersons"]["elements"] |> length == 1
|
||||||
|
@ -263,10 +286,10 @@ defmodule Mobilizon.GraphQL.Resolvers.SearchTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "finds persons with word search", %{
|
test "finds persons with word search", %{
|
||||||
conn: conn,
|
conn: conn
|
||||||
user: user
|
|
||||||
} do
|
} do
|
||||||
actor = insert(:actor, user: user, preferred_username: "person", name: "I like pineapples")
|
user = insert(:user, role: :moderator)
|
||||||
|
actor = insert(:actor, preferred_username: "person", name: "I like pineapples")
|
||||||
insert(:actor, preferred_username: "group", type: :Group, name: "pineapple group")
|
insert(:actor, preferred_username: "group", type: :Group, name: "pineapple group")
|
||||||
event1 = insert(:event, title: "Pineapple fashion week")
|
event1 = insert(:event, title: "Pineapple fashion week")
|
||||||
event2 = insert(:event, title: "I love pineAPPLE")
|
event2 = insert(:event, title: "I love pineAPPLE")
|
||||||
|
@ -276,7 +299,9 @@ defmodule Mobilizon.GraphQL.Resolvers.SearchTest do
|
||||||
Workers.BuildSearch.insert_search_event(event3)
|
Workers.BuildSearch.insert_search_event(event3)
|
||||||
|
|
||||||
res =
|
res =
|
||||||
AbsintheHelpers.graphql_query(conn,
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
query: @search_persons_query,
|
query: @search_persons_query,
|
||||||
variables: %{term: "pineapple"}
|
variables: %{term: "pineapple"}
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,7 +5,12 @@ defmodule Mobilizon.GraphQL.Resolvers.TagTest do
|
||||||
|
|
||||||
alias Mobilizon.GraphQL.AbsintheHelpers
|
alias Mobilizon.GraphQL.AbsintheHelpers
|
||||||
|
|
||||||
describe "Tag Resolver" do
|
setup do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, user: user}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "list_tags/3" do
|
||||||
@tags_query """
|
@tags_query """
|
||||||
query Tags($filter: String) {
|
query Tags($filter: String) {
|
||||||
tags(filter: $filter) {
|
tags(filter: $filter) {
|
||||||
|
@ -21,7 +26,16 @@ defmodule Mobilizon.GraphQL.Resolvers.TagTest do
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
test "list_tags/3 returns the list of tags", %{conn: conn} do
|
test "requires being logged-in", %{conn: conn, user: user} do
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(query: @tags_query)
|
||||||
|
|
||||||
|
assert res["errors"] == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns the list of tags", %{conn: conn, user: user} do
|
||||||
tag1 = insert(:tag)
|
tag1 = insert(:tag)
|
||||||
tag2 = insert(:tag)
|
tag2 = insert(:tag)
|
||||||
tag3 = insert(:tag)
|
tag3 = insert(:tag)
|
||||||
|
@ -30,8 +44,10 @@ defmodule Mobilizon.GraphQL.Resolvers.TagTest do
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|> AbsintheHelpers.graphql_query(query: @tags_query)
|
|> AbsintheHelpers.graphql_query(query: @tags_query)
|
||||||
|
|
||||||
|
assert res["errors"] == nil
|
||||||
tags = res["data"]["tags"]
|
tags = res["data"]["tags"]
|
||||||
assert tags |> length == 3
|
assert tags |> length == 3
|
||||||
|
|
||||||
|
@ -46,15 +62,17 @@ defmodule Mobilizon.GraphQL.Resolvers.TagTest do
|
||||||
|> MapSet.new()
|
|> MapSet.new()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "list_tags/3 returns tags for a filter", %{conn: conn} do
|
test "returns tags for a filter", %{conn: conn, user: user} do
|
||||||
tag1 = insert(:tag, title: "PineApple", slug: "pineapple")
|
tag1 = insert(:tag, title: "PineApple", slug: "pineapple")
|
||||||
tag2 = insert(:tag, title: "sexy pineapple", slug: "sexy-pineapple")
|
tag2 = insert(:tag, title: "sexy pineapple", slug: "sexy-pineapple")
|
||||||
_tag3 = insert(:tag)
|
_tag3 = insert(:tag)
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|> AbsintheHelpers.graphql_query(query: @tags_query, variables: %{filter: "apple"})
|
|> AbsintheHelpers.graphql_query(query: @tags_query, variables: %{filter: "apple"})
|
||||||
|
|
||||||
|
assert res["errors"] == nil
|
||||||
tags = res["data"]["tags"]
|
tags = res["data"]["tags"]
|
||||||
assert tags |> length == 2
|
assert tags |> length == 2
|
||||||
assert [tag1.id, tag2.id] == tags |> Enum.map(&String.to_integer(&1["id"]))
|
assert [tag1.id, tag2.id] == tags |> Enum.map(&String.to_integer(&1["id"]))
|
||||||
|
|
|
@ -949,63 +949,57 @@ defmodule Mobilizon.GraphQL.Resolvers.UserTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "Resolver: Refresh a token" do
|
describe "Resolver: Refresh a token" do
|
||||||
test "test refresh_token/3 with a bad token", context do
|
@refresh_token_mutation """
|
||||||
mutation = """
|
mutation RefreshToken($refreshToken: String!) {
|
||||||
mutation {
|
|
||||||
refreshToken(
|
refreshToken(
|
||||||
refreshToken: "bad_token"
|
refreshToken: $refreshToken
|
||||||
) {
|
) {
|
||||||
accessToken
|
accessToken
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
res =
|
@logged_person_query """
|
||||||
context.conn
|
query LoggedPerson {
|
||||||
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
|
||||||
|
|
||||||
assert hd(json_response(res, 200)["errors"])["message"] ==
|
|
||||||
"Cannot refresh the token"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "test refresh_token/3 with an appropriate token", context do
|
|
||||||
user = insert(:user)
|
|
||||||
insert(:actor, user: user)
|
|
||||||
{:ok, refresh_token} = Authenticator.generate_refresh_token(user)
|
|
||||||
|
|
||||||
mutation = """
|
|
||||||
mutation {
|
|
||||||
refreshToken(
|
|
||||||
refreshToken: "#{refresh_token}"
|
|
||||||
) {
|
|
||||||
accessToken
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
|
||||||
context.conn
|
|
||||||
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
|
||||||
|
|
||||||
assert json_response(res, 200)["errors"] == nil
|
|
||||||
|
|
||||||
access_token = json_response(res, 200)["data"]["refreshToken"]["accessToken"]
|
|
||||||
assert String.length(access_token) > 10
|
|
||||||
|
|
||||||
query = """
|
|
||||||
{
|
|
||||||
loggedPerson {
|
loggedPerson {
|
||||||
preferredUsername,
|
preferredUsername,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
test "test refresh_token/3 with a bad token", %{conn: conn} do
|
||||||
res =
|
res =
|
||||||
context.conn
|
AbsintheHelpers.graphql_query(conn,
|
||||||
|> Plug.Conn.put_req_header("authorization", "Bearer #{access_token}")
|
query: @refresh_token_mutation,
|
||||||
|> post("/api", AbsintheHelpers.query_skeleton(query, "logged_person"))
|
variables: [refreshToken: "bad_token"]
|
||||||
|
)
|
||||||
|
|
||||||
assert json_response(res, 200)["errors"] == nil
|
assert hd(res["errors"])["message"] ==
|
||||||
|
"Cannot refresh the token"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "test refresh_token/3 with an appropriate token", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
insert(:actor, user: user)
|
||||||
|
{:ok, refresh_token} = Authenticator.generate_refresh_token(user)
|
||||||
|
|
||||||
|
res =
|
||||||
|
AbsintheHelpers.graphql_query(conn,
|
||||||
|
query: @refresh_token_mutation,
|
||||||
|
variables: [refreshToken: refresh_token]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert res["errors"] == nil
|
||||||
|
|
||||||
|
access_token = res["data"]["refreshToken"]["accessToken"]
|
||||||
|
assert String.length(access_token) > 10
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> Plug.Conn.put_req_header("authorization", "Bearer #{access_token}")
|
||||||
|
|> AbsintheHelpers.graphql_query(query: @logged_person_query)
|
||||||
|
|
||||||
|
assert res["errors"] == nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1246,7 +1240,7 @@ defmodule Mobilizon.GraphQL.Resolvers.UserTest do
|
||||||
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
|> post("/api", AbsintheHelpers.mutation_skeleton(mutation))
|
||||||
|
|
||||||
assert hd(json_response(res, 200)["errors"])["message"] ==
|
assert hd(json_response(res, 200)["errors"])["message"] ==
|
||||||
"You need to be logged-in to change your password"
|
"You need to be logged in"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1443,7 +1437,7 @@ defmodule Mobilizon.GraphQL.Resolvers.UserTest do
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] ==
|
assert hd(res["errors"])["message"] ==
|
||||||
"You need to be logged-in to change your email"
|
"You need to be logged in"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1566,7 +1560,7 @@ defmodule Mobilizon.GraphQL.Resolvers.UserTest do
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] ==
|
assert hd(res["errors"])["message"] ==
|
||||||
"You need to be logged-in to delete your account"
|
"You need to be logged in"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,14 +25,15 @@ defmodule Mobilizon.ApplicationsTest do
|
||||||
name: "some name",
|
name: "some name",
|
||||||
client_id: "hello",
|
client_id: "hello",
|
||||||
client_secret: "secret",
|
client_secret: "secret",
|
||||||
redirect_uris: "somewhere\nelse"
|
redirect_uris: ["somewhere", "else"],
|
||||||
|
scope: "read"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert {:ok, %Application{} = application} = Applications.create_application(valid_attrs)
|
assert {:ok, %Application{} = application} = Applications.create_application(valid_attrs)
|
||||||
assert application.name == "some name"
|
assert application.name == "some name"
|
||||||
assert application.client_id == "hello"
|
assert application.client_id == "hello"
|
||||||
assert application.client_secret == "secret"
|
assert application.client_secret == "secret"
|
||||||
assert application.redirect_uris == "somewhere\nelse"
|
assert application.redirect_uris == ["somewhere", "else"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "create_application/1 with invalid data returns error changeset" do
|
test "create_application/1 with invalid data returns error changeset" do
|
||||||
|
@ -95,7 +96,8 @@ defmodule Mobilizon.ApplicationsTest do
|
||||||
valid_attrs = %{
|
valid_attrs = %{
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
application_id: application.id,
|
application_id: application.id,
|
||||||
authorization_code: "hey hello"
|
authorization_code: "hey hello",
|
||||||
|
scope: "read"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert {:ok, %ApplicationToken{} = application_token} =
|
assert {:ok, %ApplicationToken{} = application_token} =
|
||||||
|
@ -149,7 +151,11 @@ defmodule Mobilizon.ApplicationsTest do
|
||||||
|
|
||||||
import Mobilizon.ApplicationsFixtures
|
import Mobilizon.ApplicationsFixtures
|
||||||
|
|
||||||
@invalid_attrs %{}
|
@invalid_attrs %{
|
||||||
|
application_id: nil,
|
||||||
|
scope: nil,
|
||||||
|
expires_in: nil
|
||||||
|
}
|
||||||
|
|
||||||
test "list_application_device_activation/0 returns all application_device_activation" do
|
test "list_application_device_activation/0 returns all application_device_activation" do
|
||||||
application_device_activation = application_device_activation_fixture()
|
application_device_activation = application_device_activation_fixture()
|
||||||
|
@ -164,10 +170,20 @@ defmodule Mobilizon.ApplicationsTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "create_application_device_activation/1 with valid data creates a application_device_activation" do
|
test "create_application_device_activation/1 with valid data creates a application_device_activation" do
|
||||||
valid_attrs = %{}
|
application = application_fixture()
|
||||||
|
|
||||||
|
valid_attrs = %{
|
||||||
|
user_code: "hello",
|
||||||
|
device_code: "some code",
|
||||||
|
expires_in: 900,
|
||||||
|
application_id: application.id,
|
||||||
|
scope: "read"
|
||||||
|
}
|
||||||
|
|
||||||
assert {:ok, %ApplicationDeviceActivation{} = application_device_activation} =
|
assert {:ok, %ApplicationDeviceActivation{} = application_device_activation} =
|
||||||
Applications.create_application_device_activation(valid_attrs)
|
Applications.create_application_device_activation(valid_attrs)
|
||||||
|
|
||||||
|
assert application_device_activation == "read"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "create_application_device_activation/1 with invalid data returns error changeset" do
|
test "create_application_device_activation/1 with invalid data returns error changeset" do
|
||||||
|
@ -177,13 +193,18 @@ defmodule Mobilizon.ApplicationsTest do
|
||||||
|
|
||||||
test "update_application_device_activation/2 with valid data updates the application_device_activation" do
|
test "update_application_device_activation/2 with valid data updates the application_device_activation" do
|
||||||
application_device_activation = application_device_activation_fixture()
|
application_device_activation = application_device_activation_fixture()
|
||||||
update_attrs = %{}
|
|
||||||
|
update_attrs = %{
|
||||||
|
status: "success"
|
||||||
|
}
|
||||||
|
|
||||||
assert {:ok, %ApplicationDeviceActivation{} = application_device_activation} =
|
assert {:ok, %ApplicationDeviceActivation{} = application_device_activation} =
|
||||||
Applications.update_application_device_activation(
|
Applications.update_application_device_activation(
|
||||||
application_device_activation,
|
application_device_activation,
|
||||||
update_attrs
|
update_attrs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert application_device_activation == "success"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "update_application_device_activation/2 with invalid data returns error changeset" do
|
test "update_application_device_activation/2 with invalid data returns error changeset" do
|
||||||
|
|
|
@ -18,7 +18,6 @@ defmodule Mobilizon.Web.ConnCase do
|
||||||
alias Ecto.Adapters.SQL.Sandbox, as: Adapter
|
alias Ecto.Adapters.SQL.Sandbox, as: Adapter
|
||||||
|
|
||||||
alias Mobilizon.Storage.Repo
|
alias Mobilizon.Storage.Repo
|
||||||
alias Mobilizon.Users.User
|
|
||||||
|
|
||||||
alias Mobilizon.Web.Auth
|
alias Mobilizon.Web.Auth
|
||||||
|
|
||||||
|
@ -33,13 +32,20 @@ defmodule Mobilizon.Web.ConnCase do
|
||||||
# The default endpoint for testing
|
# The default endpoint for testing
|
||||||
@endpoint Mobilizon.Web.Endpoint
|
@endpoint Mobilizon.Web.Endpoint
|
||||||
|
|
||||||
def auth_conn(%Plug.Conn{} = conn, %User{} = user) do
|
def auth_conn(%Plug.Conn{} = conn, user) do
|
||||||
{:ok, token, _claims} = Auth.Guardian.encode_and_sign(user)
|
{:ok, token, _claims} = Auth.Guardian.encode_and_sign(user)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> Plug.Conn.put_req_header("authorization", "Bearer #{token}")
|
|> Plug.Conn.put_req_header("authorization", "Bearer #{token}")
|
||||||
|> Plug.Conn.put_req_header("accept", "application/json")
|
|> Plug.Conn.put_req_header("accept", "application/json")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec set_token(Plug.Conn.t(), String.t()) :: Plug.Conn.t()
|
||||||
|
def set_token(%Plug.Conn{} = conn, token) do
|
||||||
|
conn
|
||||||
|
|> Plug.Conn.put_req_header("authorization", "Bearer #{token}")
|
||||||
|
|> Plug.Conn.put_req_header("accept", "application/json")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -503,4 +503,37 @@ defmodule Mobilizon.Factory do
|
||||||
type: :string
|
type: :string
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def auth_application_factory do
|
||||||
|
%Mobilizon.Applications.Application{
|
||||||
|
name: sequence("My app"),
|
||||||
|
client_id: :crypto.strong_rand_bytes(42) |> Base.encode64() |> binary_part(0, 42),
|
||||||
|
client_secret: :crypto.strong_rand_bytes(42) |> Base.encode64() |> binary_part(0, 42),
|
||||||
|
redirect_uris: [sequence("https://someredir.uri")],
|
||||||
|
scope: "read write",
|
||||||
|
website: "https://somewebsite.com"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def auth_application_token_factory do
|
||||||
|
%Mobilizon.Applications.ApplicationToken{
|
||||||
|
authorization_code: sequence("some code"),
|
||||||
|
status: "pending",
|
||||||
|
scope: "read write",
|
||||||
|
user: build(:user),
|
||||||
|
application: build(:auth_application)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def auth_application_device_activation_factory do
|
||||||
|
%Mobilizon.Applications.ApplicationDeviceActivation{
|
||||||
|
user_code: :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8),
|
||||||
|
device_code: :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8),
|
||||||
|
status: "pending",
|
||||||
|
scope: "read write",
|
||||||
|
expires_in: 600,
|
||||||
|
user: build(:user),
|
||||||
|
application: build(:auth_application)
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,7 +16,8 @@ defmodule Mobilizon.ApplicationsFixtures do
|
||||||
name: "some name",
|
name: "some name",
|
||||||
client_id: "hello",
|
client_id: "hello",
|
||||||
client_secret: "secret",
|
client_secret: "secret",
|
||||||
redirect_uris: "somewhere\nelse"
|
redirect_uris: ["somewhere", "else"],
|
||||||
|
scope: "read"
|
||||||
})
|
})
|
||||||
|> Mobilizon.Applications.create_application()
|
|> Mobilizon.Applications.create_application()
|
||||||
|
|
||||||
|
@ -34,7 +35,9 @@ defmodule Mobilizon.ApplicationsFixtures do
|
||||||
|> Enum.into(%{
|
|> Enum.into(%{
|
||||||
application_id: application_fixture().id,
|
application_id: application_fixture().id,
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
authorization_code: "some code"
|
authorization_code: "some code",
|
||||||
|
scope: "read",
|
||||||
|
status: :pending
|
||||||
})
|
})
|
||||||
|> Mobilizon.Applications.create_application_token()
|
|> Mobilizon.Applications.create_application_token()
|
||||||
|
|
||||||
|
@ -47,7 +50,13 @@ defmodule Mobilizon.ApplicationsFixtures do
|
||||||
def application_device_activation_fixture(attrs \\ %{}) do
|
def application_device_activation_fixture(attrs \\ %{}) do
|
||||||
{:ok, application_device_activation} =
|
{:ok, application_device_activation} =
|
||||||
attrs
|
attrs
|
||||||
|> Enum.into(%{})
|
|> Enum.into(%{
|
||||||
|
user_code: "hello",
|
||||||
|
device_code: "computers",
|
||||||
|
expires_in: 600,
|
||||||
|
application_id: application_fixture().id,
|
||||||
|
scope: "read"
|
||||||
|
})
|
||||||
|> Mobilizon.Applications.create_application_device_activation()
|
|> Mobilizon.Applications.create_application_device_activation()
|
||||||
|
|
||||||
application_device_activation
|
application_device_activation
|
||||||
|
|
563
test/web/controllers/application_controller_test.exs
Normal file
563
test/web/controllers/application_controller_test.exs
Normal file
|
@ -0,0 +1,563 @@
|
||||||
|
defmodule Mobilizon.Web.ApplicationControllerTest do
|
||||||
|
use Mobilizon.Web.ConnCase
|
||||||
|
alias Mobilizon.Service.Auth.Applications
|
||||||
|
alias Mobilizon.Web.Router.Helpers, as: Routes
|
||||||
|
import Mobilizon.Factory
|
||||||
|
|
||||||
|
describe "create application" do
|
||||||
|
test "requires all parameters",
|
||||||
|
%{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> post("/apps", %{"name" => "hello"})
|
||||||
|
|
||||||
|
assert response(conn, 400) ==
|
||||||
|
"All of name, scope and redirect_uri parameters are required to create an application"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "requires valid scopes",
|
||||||
|
%{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> post("/apps", %{
|
||||||
|
"name" => "hello",
|
||||||
|
"redirect_uris" => "hello",
|
||||||
|
"scope" => "write nothing"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert response(conn, 400) ==
|
||||||
|
"The scope parameter is not a space separated list of valid scopes"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "works",
|
||||||
|
%{conn: conn} do
|
||||||
|
name = "hello"
|
||||||
|
redirect_uris = ["hello", "world"]
|
||||||
|
scope = "read write:event:create"
|
||||||
|
website = "hi"
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> post("/apps", %{
|
||||||
|
"name" => name,
|
||||||
|
"redirect_uris" => Enum.join(redirect_uris, "\n"),
|
||||||
|
"scope" => scope,
|
||||||
|
"website" => website
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"name" => ^name,
|
||||||
|
"redirect_uris" => ^redirect_uris,
|
||||||
|
"scope" => ^scope,
|
||||||
|
"website" => ^website,
|
||||||
|
"client_id" => _client_id,
|
||||||
|
"client_secret" => _client_secret
|
||||||
|
} = json_response(conn, 200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "authorize" do
|
||||||
|
test "without all required params", %{conn: conn} do
|
||||||
|
conn = get(conn, "/oauth/authorize?client_id=hello")
|
||||||
|
|
||||||
|
assert response(conn, 400) ==
|
||||||
|
"You need to specify client_id, redirect_uri, scope and state to autorize an application"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with all required params redirects to authorization page", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
get(
|
||||||
|
conn,
|
||||||
|
"/oauth/authorize?client_id=hello&redirect_uri=somewhere&state=something&scope=everything"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert redirected_to(conn) =~ "/oauth/autorize_approve"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "generate device code" do
|
||||||
|
test "without all required params", %{conn: conn} do
|
||||||
|
conn = post(conn, "/login/device/code", client_id: "hello")
|
||||||
|
|
||||||
|
assert response(conn, 400) ==
|
||||||
|
"You need to pass both client_id and scope as parameters to obtain a device code"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with an invalid client_id", %{conn: conn} do
|
||||||
|
conn = post(conn, "/login/device/code", client_id: "hello", scope: "write:event:create")
|
||||||
|
|
||||||
|
assert response(conn, 400) == "No application with this client_id was found"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with a scope not matching app registered scopes", %{conn: conn} do
|
||||||
|
{:ok, app} =
|
||||||
|
Applications.create("My app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/login/device/code", client_id: app.client_id, scope: "write:event:delete")
|
||||||
|
|
||||||
|
assert response(conn, 400) ==
|
||||||
|
"The given scope is not in the list of the app declared scopes"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with valid params gives a URL-encoded code", %{conn: conn} do
|
||||||
|
{:ok, app} =
|
||||||
|
Applications.create("My app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/login/device/code", client_id: app.client_id, scope: "write:event:create")
|
||||||
|
|
||||||
|
res = conn |> response(200) |> URI.decode_query()
|
||||||
|
|
||||||
|
verification_uri = Routes.page_url(Mobilizon.Web.Endpoint, :auth_device)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"device_code" => _device_code,
|
||||||
|
"expires_in" => "900",
|
||||||
|
"interval" => "5",
|
||||||
|
"user_code" => user_code,
|
||||||
|
"verification_uri" => ^verification_uri
|
||||||
|
} = res
|
||||||
|
|
||||||
|
assert Regex.match?(~r/^[A-Z]{4}-[A-Z]{4}$/, user_code)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with valid params and a JSON Accept header gives a JSON-encoded struct", %{conn: conn} do
|
||||||
|
{:ok, app} =
|
||||||
|
Applications.create("My app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> Plug.Conn.put_req_header("accept", "application/json")
|
||||||
|
|> post("/login/device/code", client_id: app.client_id, scope: "write:event:create")
|
||||||
|
|
||||||
|
res = json_response(conn, 200)
|
||||||
|
|
||||||
|
verification_uri = Routes.page_url(Mobilizon.Web.Endpoint, :auth_device)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"device_code" => _device_code,
|
||||||
|
"expires_in" => 900,
|
||||||
|
"interval" => 5,
|
||||||
|
"user_code" => user_code,
|
||||||
|
"verification_uri" => ^verification_uri
|
||||||
|
} = res
|
||||||
|
|
||||||
|
assert Regex.match?(~r/^[A-Z]{4}-[A-Z]{4}$/, user_code)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "generate access code for device flow" do
|
||||||
|
test "without valid parameters", %{conn: conn} do
|
||||||
|
conn = post(conn, "/oauth/token")
|
||||||
|
|
||||||
|
assert response(conn, 400) ==
|
||||||
|
"Incorrect parameters sent. You need to provide at least the grant_type and client_id parameters, depending on the grant type being used."
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with invalid client_id", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
||||||
|
client_id: "some_client_id",
|
||||||
|
device_code: "hello"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response(conn, 400) ==
|
||||||
|
"The client_id provided or the device_code associated is invalid"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with rejected authorization", %{conn: conn} do
|
||||||
|
{:ok, app} =
|
||||||
|
Applications.create("My app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
assert {:ok, _res} =
|
||||||
|
Mobilizon.Applications.create_application_device_activation(%{
|
||||||
|
device_code: "hello",
|
||||||
|
user_code: "world",
|
||||||
|
expires_in: 900,
|
||||||
|
application_id: app.id,
|
||||||
|
scope: "write:event:create write:event:update",
|
||||||
|
status: :access_denied
|
||||||
|
})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
||||||
|
client_id: app.client_id,
|
||||||
|
device_code: "hello"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response(conn, 401) == "The user rejected the requested authorization"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with incorrect device code", %{conn: conn} do
|
||||||
|
{:ok, app} =
|
||||||
|
Applications.create("My app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
assert {:ok, _res} =
|
||||||
|
Mobilizon.Applications.create_application_device_activation(%{
|
||||||
|
device_code: "hello",
|
||||||
|
user_code: "world",
|
||||||
|
expires_in: 900,
|
||||||
|
application_id: app.id,
|
||||||
|
scope: "write:event:create write:event:update",
|
||||||
|
status: "incorrect_device_code"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
||||||
|
client_id: app.client_id,
|
||||||
|
device_code: "hello"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response(conn, 400) ==
|
||||||
|
"The client_id provided or the device_code associated is invalid"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with an expired device activation", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, app} =
|
||||||
|
Applications.create("My app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
assert {:ok, _res} =
|
||||||
|
Mobilizon.Applications.create_application_device_activation(%{
|
||||||
|
device_code: "hello",
|
||||||
|
user_code: "world",
|
||||||
|
expires_in: -40,
|
||||||
|
application_id: app.id,
|
||||||
|
scope: "write:event:create write:event:update",
|
||||||
|
status: "success",
|
||||||
|
user_id: user.id
|
||||||
|
})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
||||||
|
client_id: app.client_id,
|
||||||
|
device_code: "hello"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response(conn, 400) ==
|
||||||
|
"The given device_code has expired"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with valid params", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, app} =
|
||||||
|
Applications.create("My app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
assert {:ok, _res} =
|
||||||
|
Mobilizon.Applications.create_application_device_activation(%{
|
||||||
|
device_code: "hello",
|
||||||
|
user_code: "world",
|
||||||
|
expires_in: 600,
|
||||||
|
application_id: app.id,
|
||||||
|
scope: "write:event:create write:event:update",
|
||||||
|
status: "success",
|
||||||
|
user_id: user.id
|
||||||
|
})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
||||||
|
client_id: app.client_id,
|
||||||
|
device_code: "hello"
|
||||||
|
)
|
||||||
|
|
||||||
|
res = conn |> response(200) |> URI.decode_query()
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"access_token" => _access_token,
|
||||||
|
"expires_in" => "28800",
|
||||||
|
"refresh_token" => _refresh_token,
|
||||||
|
"refresh_token_expires_in" => "15724800",
|
||||||
|
"scope" => "write:event:create write:event:update",
|
||||||
|
"token_type" => "bearer"
|
||||||
|
} = res
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with valid params as JSON", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, app} =
|
||||||
|
Applications.create("My app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
assert {:ok, _res} =
|
||||||
|
Mobilizon.Applications.create_application_device_activation(%{
|
||||||
|
device_code: "hello",
|
||||||
|
user_code: "world",
|
||||||
|
expires_in: 600,
|
||||||
|
application_id: app.id,
|
||||||
|
scope: "write:event:create write:event:update",
|
||||||
|
status: "success",
|
||||||
|
user_id: user.id
|
||||||
|
})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> Plug.Conn.put_req_header("accept", "application/json")
|
||||||
|
|> post("/oauth/token",
|
||||||
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
||||||
|
client_id: app.client_id,
|
||||||
|
device_code: "hello"
|
||||||
|
)
|
||||||
|
|
||||||
|
res = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"access_token" => _access_token,
|
||||||
|
"expires_in" => 28_800,
|
||||||
|
"refresh_token" => _refresh_token,
|
||||||
|
"refresh_token_expires_in" => 15_724_800,
|
||||||
|
"scope" => "write:event:create write:event:update",
|
||||||
|
"token_type" => "bearer"
|
||||||
|
} = res
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "generate access code for authorization flow" do
|
||||||
|
test "with invalid client_id", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "authorization_code",
|
||||||
|
client_id: "some_client_id",
|
||||||
|
client_secret: "some_client_secret",
|
||||||
|
code: "hello",
|
||||||
|
redirect_uri: "some redirect uri",
|
||||||
|
scope: "hello"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert json_response(conn, 200)["details"] ==
|
||||||
|
"No application was found with this client_id"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with invalid redirect_uri", %{conn: conn} do
|
||||||
|
{:ok, app} =
|
||||||
|
Applications.create("My app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "authorization_code",
|
||||||
|
client_id: app.client_id,
|
||||||
|
client_secret: app.client_secret,
|
||||||
|
code: "hello",
|
||||||
|
redirect_uri: "nope",
|
||||||
|
scope: "hello"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert json_response(conn, 200)["details"] ==
|
||||||
|
"This redirect URI is not allowed"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with invalid code", %{conn: conn} do
|
||||||
|
{:ok, app} =
|
||||||
|
Applications.create("My app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "authorization_code",
|
||||||
|
client_id: app.client_id,
|
||||||
|
client_secret: app.client_secret,
|
||||||
|
code: "hello",
|
||||||
|
redirect_uri: "hello",
|
||||||
|
scope: "hello"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert json_response(conn, 200)["details"] ==
|
||||||
|
"The provided code is invalid or expired"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with invalid client secret", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, app} =
|
||||||
|
Applications.create("My app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
Mobilizon.Applications.create_application_token(%{
|
||||||
|
user_id: user.id,
|
||||||
|
application_id: app.id,
|
||||||
|
authorization_code: "hi there",
|
||||||
|
scope: "write:event:create write:event:update"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "authorization_code",
|
||||||
|
client_id: app.client_id,
|
||||||
|
client_secret: "not the client secret",
|
||||||
|
code: "hi there",
|
||||||
|
redirect_uri: "hello",
|
||||||
|
scope: "write:event:create write:event:update"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert json_response(conn, 200)["details"] ==
|
||||||
|
"The provided client_secret is invalid"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with an authorization code matching a different app", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, app} =
|
||||||
|
Applications.create("My app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
{:ok, app2} =
|
||||||
|
Applications.create("My other app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
Mobilizon.Applications.create_application_token(%{
|
||||||
|
user_id: user.id,
|
||||||
|
application_id: app2.id,
|
||||||
|
authorization_code: "hi there",
|
||||||
|
scope: "write:event:create write:event:update"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "authorization_code",
|
||||||
|
client_id: app.client_id,
|
||||||
|
client_secret: app.client_id,
|
||||||
|
code: "hi there",
|
||||||
|
redirect_uri: "hello",
|
||||||
|
scope: "write:event:create write:event:update"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert json_response(conn, 200)["details"] ==
|
||||||
|
"The provided client_id does not match the provided code"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with valid params", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, app} =
|
||||||
|
Applications.create("My app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
Mobilizon.Applications.create_application_token(%{
|
||||||
|
user_id: user.id,
|
||||||
|
application_id: app.id,
|
||||||
|
authorization_code: "hi there",
|
||||||
|
scope: "write:event:create write:event:update"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "authorization_code",
|
||||||
|
client_id: app.client_id,
|
||||||
|
client_secret: app.client_secret,
|
||||||
|
code: "hi there",
|
||||||
|
redirect_uri: "hello",
|
||||||
|
scope: "write:event:create write:event:update"
|
||||||
|
)
|
||||||
|
|
||||||
|
res = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"access_token" => _access_token,
|
||||||
|
"expires_in" => 28_800,
|
||||||
|
"refresh_token" => _refresh_token,
|
||||||
|
"refresh_token_expires_in" => 15_724_800,
|
||||||
|
"scope" => "write:event:create write:event:update",
|
||||||
|
"token_type" => "bearer"
|
||||||
|
} = res
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "generate new access code from refresh code" do
|
||||||
|
test "with invalid refresh token", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "refresh_token",
|
||||||
|
client_id: "hello",
|
||||||
|
client_secret: "secret",
|
||||||
|
refresh_token: "none"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response(conn, 400) ==
|
||||||
|
"Invalid refresh token provided"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with invalid client credentials", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, app} =
|
||||||
|
Applications.create("My app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
Mobilizon.Applications.create_application_token(%{
|
||||||
|
user_id: user.id,
|
||||||
|
application_id: app.id,
|
||||||
|
authorization_code: "hi there",
|
||||||
|
scope: "write:event:create write:event:update"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "authorization_code",
|
||||||
|
client_id: app.client_id,
|
||||||
|
client_secret: app.client_secret,
|
||||||
|
code: "hi there",
|
||||||
|
redirect_uri: "hello",
|
||||||
|
scope: "write:event:create write:event:update"
|
||||||
|
)
|
||||||
|
|
||||||
|
res = json_response(conn, 200)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "refresh_token",
|
||||||
|
client_id: "hello",
|
||||||
|
client_secret: "secret",
|
||||||
|
refresh_token: res["refresh_token"]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response(conn, 400) ==
|
||||||
|
"Invalid client credentials provided"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with valid params", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, app} =
|
||||||
|
Applications.create("My app", ["hello"], "write:event:create write:event:update")
|
||||||
|
|
||||||
|
Mobilizon.Applications.create_application_token(%{
|
||||||
|
user_id: user.id,
|
||||||
|
application_id: app.id,
|
||||||
|
authorization_code: "hi there",
|
||||||
|
scope: "write:event:create write:event:update"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "authorization_code",
|
||||||
|
client_id: app.client_id,
|
||||||
|
client_secret: app.client_secret,
|
||||||
|
code: "hi there",
|
||||||
|
redirect_uri: "hello",
|
||||||
|
scope: "write:event:create write:event:update"
|
||||||
|
)
|
||||||
|
|
||||||
|
res = json_response(conn, 200)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token",
|
||||||
|
grant_type: "refresh_token",
|
||||||
|
client_id: app.client_id,
|
||||||
|
client_secret: app.client_secret,
|
||||||
|
refresh_token: res["refresh_token"]
|
||||||
|
)
|
||||||
|
|
||||||
|
res = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"access_token" => _access_token,
|
||||||
|
"expires_in" => 28_800,
|
||||||
|
"refresh_token" => _refresh_token,
|
||||||
|
"refresh_token_expires_in" => 15_724_800,
|
||||||
|
"scope" => "write:event:create write:event:update",
|
||||||
|
"token_type" => "bearer"
|
||||||
|
} = res
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue