From a7753e6a5c5a627c78f5b9a420a710032b1977d2 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Tue, 27 Jul 2021 19:55:17 +0200 Subject: [PATCH 1/5] Refactor refreshing token Signed-off-by: Thomas Citharel --- js/src/vue-apollo.ts | 107 +++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 61 deletions(-) diff --git a/js/src/vue-apollo.ts b/js/src/vue-apollo.ts index 6ae0ee0a6..9b1a7714c 100644 --- a/js/src/vue-apollo.ts +++ b/js/src/vue-apollo.ts @@ -6,9 +6,9 @@ import { ApolloClient, ApolloLink, defaultDataIdFromObject, + fromPromise, InMemoryCache, NormalizedCacheObject, - Observable, split, } from "@apollo/client/core"; import buildCurrentUserResolver from "@/apollo/user"; @@ -31,8 +31,8 @@ import { GraphQLError } from "graphql"; // Install the vue plugin Vue.use(VueApollo); -let refreshingTokenPromise: Promise | undefined; -let alreadyRefreshedToken = false; +let isRefreshing = false; +let pendingRequests: any[] = []; // Endpoints const httpServer = GRAPHQL_API_ENDPOINT || "http://localhost:4000"; @@ -92,32 +92,55 @@ const link = split( uploadLink ); +const resolvePendingRequests = () => { + pendingRequests.map((callback) => callback()); + pendingRequests = []; +}; + const errorLink = onError( ({ graphQLErrors, networkError, forward, operation }) => { - if ( - isServerError(networkError) && - networkError?.statusCode === 401 && - !alreadyRefreshedToken - ) { - if (!refreshingTokenPromise) - refreshingTokenPromise = refreshAccessToken(apolloClient); + if (isServerError(networkError) && networkError?.statusCode === 401) { + let forwardOperation; - return promiseToObservable(refreshingTokenPromise).flatMap(() => { - refreshingTokenPromise = undefined; - alreadyRefreshedToken = true; + if (!isRefreshing) { + isRefreshing = true; - const context = operation.getContext(); - const oldHeaders = context.headers; + forwardOperation = fromPromise( + refreshAccessToken(apolloClient) + .then(() => { + resolvePendingRequests(); - operation.setContext({ - headers: { - ...oldHeaders, - authorization: generateTokenHeader(), - }, - }); + const context = operation.getContext(); + const oldHeaders = context.headers; - return forward(operation); - }); + operation.setContext({ + headers: { + ...oldHeaders, + authorization: generateTokenHeader(), + }, + }); + return true; + }) + .catch(() => { + pendingRequests = []; + logout(apolloClient); + return; + }) + .finally(() => { + isRefreshing = false; + }) + ).filter((value) => Boolean(value)); + } else { + forwardOperation = fromPromise( + new Promise((resolve) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + pendingRequests.push(() => resolve()); + }) + ); + } + + return forwardOperation.flatMap(() => forward(operation)); } if (graphQLErrors) { @@ -171,41 +194,3 @@ export default new VueApollo({ console.error(error); }, }); - -// Thanks: https://github.com/apollographql/apollo-link/issues/747#issuecomment-502676676 -const promiseToObservable = (promise: Promise) => - new Observable((subscriber) => { - promise.then( - (value) => { - if (subscriber.closed) { - return; - } - subscriber.next(value); - subscriber.complete(); - }, - (err) => { - console.error("Cannot refresh token.", err); - - subscriber.error(err); - logout(apolloClient); - } - ); - }); - -// Manually call this when user log in -// export function onLogin(apolloClient) { -// if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient); -// } - -// Manually call this when user log out -// export async function onLogout() { -// if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient); -// We don't reset store because we rely on currentUser & currentActor -// which are in the cache (even null). Maybe try to rerun cache init after resetStore? -// try { -// await apolloClient.resetStore(); -// } catch (e) { -// // eslint-disable-next-line no-console -// console.log('%cError on cache reset (logout)', 'color: orange;', e.message); -// } -// } From bd71dd6cf1aa2de35037072eec608dd18b66ee83 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Tue, 3 Aug 2021 14:58:06 +0200 Subject: [PATCH 2/5] Handle SSL being already started in LDAP connection Signed-off-by: Thomas Citharel --- lib/service/auth/ldap_authenticator.ex | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/service/auth/ldap_authenticator.ex b/lib/service/auth/ldap_authenticator.ex index 7be6f0995..d4816a30b 100644 --- a/lib/service/auth/ldap_authenticator.ex +++ b/lib/service/auth/ldap_authenticator.ex @@ -215,6 +215,9 @@ defmodule Mobilizon.Service.Auth.LDAPAuthenticator do :ok -> :ok + {:error, :tls_already_started} -> + :ok + error -> Logger.error("Could not start TLS: #{inspect(error)}") end From b53d8d54ef022c8d649e038fdbf6de32e6adac0e Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Tue, 3 Aug 2021 14:58:42 +0200 Subject: [PATCH 3/5] Fix apollo cache issue when registrering first profile Signed-off-by: Thomas Citharel --- js/src/views/Account/Register.vue | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/js/src/views/Account/Register.vue b/js/src/views/Account/Register.vue index 8dc97b895..95a770e9b 100644 --- a/js/src/views/Account/Register.vue +++ b/js/src/views/Account/Register.vue @@ -129,6 +129,7 @@ import RouteName from "../../router/name"; import { changeIdentity } from "../../utils/auth"; import identityEditionMixin from "../../mixins/identityEdition"; import { ApolloCache, FetchResult } from "@apollo/client/core"; +import { ActorType } from "@/types/enums"; @Component({ apollo: { @@ -180,8 +181,18 @@ export default class Register extends mixins(identityEditionMixin) { }); if (identitiesData && localData) { - identitiesData.identities.push(localData.registerPerson); - store.writeQuery({ query: IDENTITIES, data: identitiesData }); + const newPersonData = { + ...localData.registerPerson, + type: ActorType.PERSON, + }; + + store.writeQuery({ + query: IDENTITIES, + data: { + ...identitiesData, + identities: [...identitiesData.identities, newPersonData], + }, + }); } } }, From 0110124b3278cc5a4bb9cde0e6636b053053b16e Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Tue, 3 Aug 2021 15:01:22 +0200 Subject: [PATCH 4/5] Add an await before a router push when login Signed-off-by: Thomas Citharel --- js/src/views/User/Login.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/views/User/Login.vue b/js/src/views/User/Login.vue index b21f28443..7d68efe42 100644 --- a/js/src/views/User/Login.vue +++ b/js/src/views/User/Login.vue @@ -259,7 +259,7 @@ export default class Login extends Vue { await initializeCurrentActor(this.$apollo.provider.defaultClient); } catch (err) { if (err instanceof NoIdentitiesException) { - this.$router.push({ + await this.$router.push({ name: RouteName.REGISTER_PROFILE, params: { email: this.currentUser.email, From 549b82c94a017844bae67f08e980a7366825b025 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Tue, 3 Aug 2021 15:13:30 +0200 Subject: [PATCH 5/5] Remove unused getter Signed-off-by: Thomas Citharel --- js/src/views/Event/Event.vue | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/js/src/views/Event/Event.vue b/js/src/views/Event/Event.vue index 8664ce6aa..69fea48ff 100755 --- a/js/src/views/Event/Event.vue +++ b/js/src/views/Event/Event.vue @@ -1309,29 +1309,6 @@ export default class Event extends EventMixin { } return null; } - - get shouldShowParticipationButton(): boolean { - // So that people can cancel their participation - if ( - this.actorIsParticipant || - (this.config.anonymous.participation.allowed && - this.anonymousParticipation) - ) - return true; - - // You can participate to draft or cancelled events - if (this.event.draft || this.event.status === EventStatus.CANCELLED) - return false; - - // Organizer can't participate - if (this.actorIsOrganizer) return false; - - // If capacity is OK - if (this.eventCapacityOK) return true; - - // Else - return false; - } }