diff options
Diffstat (limited to 'packages/anastasis-webui/src/pages/home/AddingProviderScreen/index.ts')
-rw-r--r-- | packages/anastasis-webui/src/pages/home/AddingProviderScreen/index.ts | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/index.ts b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/index.ts new file mode 100644 index 000000000..ed8301d65 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/index.ts @@ -0,0 +1,131 @@ +/* + This file is part of GNU Anastasis + (C) 2021-2022 Anastasis SARL + + GNU Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + GNU Anastasis; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +import { AuthenticationProviderStatus } from "@gnu-taler/anastasis-core"; +import InvalidState from "../../../components/InvalidState.js"; +import NoReducer from "../../../components/NoReducer.js"; +import { Notification } from "../../../components/Notifications.js"; +import { compose, StateViewMap } from "../../../utils/index.js"; +import useComponentState from "./state.js"; +import { WithoutProviderType, WithProviderType } from "./views.js"; + +export type AuthProvByStatusMap = Record< + AuthenticationProviderStatus["status"], + (AuthenticationProviderStatus & { url: string })[] +>; + +export type State = NoReducer | InvalidState | WithType | WithoutType; + +export interface NoReducer { + status: "no-reducer"; +} +export interface InvalidState { + status: "invalid-state"; +} + +interface CommonProps { + addProvider?: () => Promise<void>; + deleteProvider: (url: string) => Promise<void>; + authProvidersByStatus: AuthProvByStatusMap; + error: string | undefined; + onCancel: () => Promise<void>; + testing: boolean; + setProviderURL: (url: string) => Promise<void>; + providerURL: string; + errors: string | undefined; + notifications: Notification[]; +} + +export interface WithType extends CommonProps { + status: "with-type"; + providerLabel: string; +} +export interface WithoutType extends CommonProps { + status: "without-type"; +} + +const map: StateViewMap<State> = { + "no-reducer": NoReducer, + "invalid-state": InvalidState, + "with-type": WithProviderType, + "without-type": WithoutProviderType, +}; + +export default compose("AddingProviderScreen", useComponentState, map); + +const providerResponseCache = new Map<string, any>(); // `any` is the return type of res.json() +export async function testProvider( + url: string, + expectedMethodType?: string, +): Promise<void> { + const testFatalPrefix = `Encountered a fatal error whilst testing the provider ${url}`; + let configUrl = ""; + try { + configUrl = new URL("config", url).href; + } catch (error) { + throw new Error(`${testFatalPrefix}: Invalid Provider URL: ${url} +Error: ${error}`); + } + // TODO: look into using core.getProviderInfo :) + const providerHasUrl = providerResponseCache.has(url); + const json = providerHasUrl + ? providerResponseCache.get(url) + : await fetch(configUrl) + .catch((error) => { + throw new Error(`${testFatalPrefix}: Could not connect: ${error} +Please check the URL.`); + }) + .then(async (response) => { + if (!response.ok) + throw new Error( + `${testFatalPrefix}: The server ${response.url} responded with a non-2xx response.`, + ); + try { + return await response.json(); + } catch (error) { + throw new Error( + `${testFatalPrefix}: The server responded with malformed JSON.\nError: ${error}`, + ); + } + }); + if (typeof json !== "object") + throw new Error( + `${testFatalPrefix}: Did not get an object after decoding.`, + ); + if (!("name" in json) || json.name !== "anastasis") { + throw new Error( + `${testFatalPrefix}: The provider does not appear to be an Anastasis provider. Please check the provider's URL.`, + ); + } + if (!("methods" in json) || !Array.isArray(json.methods)) { + throw new Error( + "This provider doesn't have authentication method. Please check the provider's URL and ensure it is properly configured.", + ); + } + if (!providerHasUrl) providerResponseCache.set(url, json); + if (!expectedMethodType) { + return; + } + let found = false; + for (let i = 0; i < json.methods.length && !found; i++) { + found = json.methods[i].type === expectedMethodType; + } + if (!found) { + throw new Error( + `${testFatalPrefix}: This provider does not support authentication method ${expectedMethodType}`, + ); + } + return; +} |