diff options
Diffstat (limited to 'front-end/src/store')
-rw-r--r-- | front-end/src/store/socialMfaPlugin.ts | 74 | ||||
-rw-r--r-- | front-end/src/store/websiteLookup.ts | 74 |
2 files changed, 106 insertions, 42 deletions
diff --git a/front-end/src/store/socialMfaPlugin.ts b/front-end/src/store/socialMfaPlugin.ts index d8d7bb1..e0ec972 100644 --- a/front-end/src/store/socialMfaPlugin.ts +++ b/front-end/src/store/socialMfaPlugin.ts @@ -1,26 +1,19 @@ -// Copyright (C) 2024 Vaughn Nugent -// -// This program 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 of the -// License, or (at your option) any later version. -// -// This program 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 this program. If not, see <https://www.gnu.org/licenses/>. import 'pinia' import { MaybeRef } from 'vue'; -import { useSocialOauthLogin, useUser, SocialOAuthPortal, fromPortals, useAxios } from '@vnuge/vnlib.browser' +import { + useUser, + useOauthLogin, + useSocialDefaultLogout, + fetchSocialPortals, + fromSocialPortals, + fromSocialConnections, +} from '@vnuge/vnlib.browser' import { get } from '@vueuse/core'; import { PiniaPluginContext, PiniaPlugin, storeToRefs } from 'pinia' import { defer } from 'lodash-es'; -type SocialMfaPlugin = ReturnType<typeof useSocialOauthLogin> +type SocialMfaPlugin = ReturnType<typeof useOauthLogin> declare module 'pinia' { export interface PiniaCustomProperties { @@ -35,43 +28,40 @@ export const socialMfaPlugin = (portalEndpoint?: MaybeRef<string>): PiniaPlugin const { } = storeToRefs(store) const { logout } = useUser() - /** - * Override the logout function to default to a social logout, - * if the social logout fails, then we will logout the user - */ - const setLogoutMethod = (socialOauth: SocialMfaPlugin) => { - const logoutFunc = socialOauth.logout; + //Create social login from available portals + const defaultSocial = useSocialDefaultLogout( + useOauthLogin([]), + logout //fallback to default logout + ); - (socialOauth as any).logout = async () => { - if (await logoutFunc() === false) { - await logout() - } - } - } - - const _loadPromise = new Promise<SocialMfaPlugin>((resolve, reject) => { + const _loadPromise = new Promise<SocialMfaPlugin>((resolve, _) => { - if(get(portalEndpoint) == null) { - const socialOauth = useSocialOauthLogin([]) - setLogoutMethod(socialOauth) - return resolve(socialOauth) + if (get(portalEndpoint) == null) { + return resolve(defaultSocial) } + /* + Try to load social methods from server, if it fails, then we will + fall back to default + */ + defer(async () => { + try { - //Get axios instance - const axios = useAxios(null) - //Get all enabled portals - const { data } = await axios.get<SocialOAuthPortal[]>(get(portalEndpoint)!); - //Setup social providers from server portals - const socialOauth = useSocialOauthLogin(fromPortals(data)); - setLogoutMethod(socialOauth); + const portals = await fetchSocialPortals(get(portalEndpoint)!); + const social = fromSocialPortals(portals); + const methods = fromSocialConnections(social); + + //Create social login from available portals + const login = useOauthLogin(methods); + const socialOauth = useSocialDefaultLogout(login, logout); resolve(socialOauth) } catch (error) { - reject(error) + //Let failure fall back to default + resolve(defaultSocial) } }) }) diff --git a/front-end/src/store/websiteLookup.ts b/front-end/src/store/websiteLookup.ts new file mode 100644 index 0000000..7d4f3ca --- /dev/null +++ b/front-end/src/store/websiteLookup.ts @@ -0,0 +1,74 @@ + +import 'pinia' +import { MaybeRef, Ref, shallowRef, watch } from 'vue'; +import { WebMessage, apiCall, useAxios } from '@vnuge/vnlib.browser' +import { get, set } from '@vueuse/core'; +import { PiniaPluginContext, PiniaPlugin, storeToRefs } from 'pinia' +import { defer, noop } from 'lodash-es'; + +export interface WebsiteLookupResult { + title: string | undefined, + description: string | undefined, + keywords: string[] | undefined, +} + +export interface LookupApi{ + isSupported: Ref<boolean>, + timeout: Ref<number>, + execLookup(url:string): Promise<WebsiteLookupResult> +} + +declare module 'pinia' { + export interface PiniaCustomProperties { + websiteLookup:{ + isSupported: boolean, + execLookup(url: string): Promise<WebsiteLookupResult> + } + } +} + +const urlToBase64UrlEncoded = (url: string) => { + return btoa(url) + .replace(/-/g, '+') + .replace(/_/g, '/') + .replace(/\./g, '=') //Fix padding +} + +export const siteLookupPlugin = (lookupEndpoint: MaybeRef<string>, to: number): PiniaPlugin => { + + return ({ store }: PiniaPluginContext) => { + + const { loggedIn } = storeToRefs(store) + const axios = useAxios(null) + + const isSupported = shallowRef(false) + const timeout = shallowRef(to) + + const checkIsSupported = () => { + return apiCall(async () => { + //Execute test with the 'support' query parameter + const { data } = await axios.get<WebMessage>(`${get(lookupEndpoint)}?support`) + set(isSupported, data.success) + }); + } + + const execLookup = async (url:string) => { + const base64Url = urlToBase64UrlEncoded(url) + + //Execute test with the 'support' query parameter + const { data } = await axios.get<WebMessage<WebsiteLookupResult>>(`${get(lookupEndpoint)}?timeout=${get(timeout)}&url=${base64Url}`) + return data.getResultOrThrow(); + } + + //If login status changes, recheck support + watch([loggedIn], ([li]) => li ? defer(checkIsSupported) : noop(), { immediate: true }) + + return { + websiteLookup: { + isSupported, + execLookup, + timeout + } as LookupApi + } as any + } +}
\ No newline at end of file |