From e69d5a6253167430e9abb34a720d49f7fd2000f7 Mon Sep 17 00:00:00 2001 From: Joel Jacob Stephen <70131076+JoelJacobStephen@users.noreply.github.com> Date: Fri, 8 Mar 2024 15:18:53 +0530 Subject: [PATCH] feat(sh-admin): introducing additional SSO related server configurations to dashboard (#3737) Co-authored-by: jamesgeorge007 --- packages/hoppscotch-sh-admin/locales/en.json | 3 + .../hoppscotch-sh-admin/src/components.d.ts | 6 + .../src/components/settings/AuthProvider.vue | 109 ++++++++++++------ .../components/settings/SmtpConfiguration.vue | 4 +- .../src/composables/useConfigHandler.ts | 100 +++++++++++----- 5 files changed, 153 insertions(+), 69 deletions(-) diff --git a/packages/hoppscotch-sh-admin/locales/en.json b/packages/hoppscotch-sh-admin/locales/en.json index c2c4b15658..a9f2bdfeb4 100644 --- a/packages/hoppscotch-sh-admin/locales/en.json +++ b/packages/hoppscotch-sh-admin/locales/en.json @@ -10,9 +10,12 @@ }, "configs": { "auth_providers": { + "callback_url": "CALLBACK URL", "client_id": "CLIENT ID", "client_secret": "CLIENT SECRET", "description": "Configure authentication providers for your server", + "scope": "SCOPE", + "tenant": "TENANT", "title": "Authentication Providers", "update_failure": "Failed to update authentication provider configurations!!" }, diff --git a/packages/hoppscotch-sh-admin/src/components.d.ts b/packages/hoppscotch-sh-admin/src/components.d.ts index 17e02a1c02..671efba5f9 100644 --- a/packages/hoppscotch-sh-admin/src/components.d.ts +++ b/packages/hoppscotch-sh-admin/src/components.d.ts @@ -17,13 +17,19 @@ declare module '@vue/runtime-core' { HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary'] HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary'] HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor'] + HoppSmartAutoComplete: typeof import('@hoppscotch/ui')['HoppSmartAutoComplete'] HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal'] HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput'] HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem'] HoppSmartLink: typeof import('@hoppscotch/ui')['HoppSmartLink'] + HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal'] HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture'] HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner'] + HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab'] + HoppSmartTable: typeof import('@hoppscotch/ui')['HoppSmartTable'] + HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs'] HoppSmartToggle: typeof import('@hoppscotch/ui')['HoppSmartToggle'] + IconLucideChevronDown: typeof import('~icons/lucide/chevron-down')['default'] IconLucideInbox: typeof import('~icons/lucide/inbox')['default'] SettingsAuthProvider: typeof import('./components/settings/AuthProvider.vue')['default'] SettingsConfigurations: typeof import('./components/settings/Configurations.vue')['default'] diff --git a/packages/hoppscotch-sh-admin/src/components/settings/AuthProvider.vue b/packages/hoppscotch-sh-admin/src/components/settings/AuthProvider.vue index a4053a278e..0b01483e92 100644 --- a/packages/hoppscotch-sh-admin/src/components/settings/AuthProvider.vue +++ b/packages/hoppscotch-sh-admin/src/components/settings/AuthProvider.vue @@ -35,25 +35,29 @@ :key="field.key" class="mt-5" > - - - - - + @@ -86,47 +90,76 @@ const workingConfigs = useVModel(props, 'config', emit); const capitalize = (text: string) => text.charAt(0).toUpperCase() + text.slice(1); -// Masking sensitive fields -type Field = { +// Union type for all possible field keys +type ProviderFieldKeys = keyof ProviderFields; + +type ProviderFields = { + [Field in keyof Config['providers'][SsoAuthProviders]['fields']]: boolean; +} & Partial<{ tenant: boolean }>; + +type ProviderFieldMetadata = { name: string; - key: keyof Config['providers']['google' | 'github' | 'microsoft']['fields']; + key: ProviderFieldKeys; + applicableProviders: SsoAuthProviders[]; }; -const providerConfigFields = reactive([ - { name: t('configs.auth_providers.client_id'), key: 'client_id' }, - { name: t('configs.auth_providers.client_secret'), key: 'client_secret' }, -]); +const providerConfigFields = [ + { + name: t('configs.auth_providers.client_id'), + key: 'client_id', + applicableProviders: ['google', 'github', 'microsoft'], + }, + { + name: t('configs.auth_providers.client_secret'), + key: 'client_secret', + applicableProviders: ['google', 'github', 'microsoft'], + }, + { + name: t('configs.auth_providers.callback_url'), + key: 'callback_url', + applicableProviders: ['google', 'github', 'microsoft'], + }, + { + name: t('configs.auth_providers.scope'), + key: 'scope', + applicableProviders: ['google', 'github', 'microsoft'], + }, + { + name: t('configs.auth_providers.tenant'), + key: 'tenant', + applicableProviders: ['microsoft'], + }, +]; -const maskState = reactive({ +const maskState = reactive>({ google: { client_id: true, client_secret: true, + callback_url: true, + scope: true, }, github: { client_id: true, client_secret: true, + callback_url: true, + scope: true, }, microsoft: { client_id: true, client_secret: true, + callback_url: true, + scope: true, + tenant: true, }, }); const toggleMask = ( provider: SsoAuthProviders, - fieldKey: keyof Config['providers'][ - | 'google' - | 'github' - | 'microsoft']['fields'] + fieldKey: ProviderFieldKeys ) => { maskState[provider][fieldKey] = !maskState[provider][fieldKey]; }; -const isMasked = ( - provider: SsoAuthProviders, - fieldKey: keyof Config['providers'][ - | 'google' - | 'github' - | 'microsoft']['fields'] -) => maskState[provider][fieldKey]; +const isMasked = (provider: SsoAuthProviders, fieldKey: ProviderFieldKeys) => + maskState[provider][fieldKey]; diff --git a/packages/hoppscotch-sh-admin/src/components/settings/SmtpConfiguration.vue b/packages/hoppscotch-sh-admin/src/components/settings/SmtpConfiguration.vue index d1c2b9442f..743b1650d1 100644 --- a/packages/hoppscotch-sh-admin/src/components/settings/SmtpConfiguration.vue +++ b/packages/hoppscotch-sh-admin/src/components/settings/SmtpConfiguration.vue @@ -33,13 +33,13 @@ class="mt-5" > - + + infraConfigs.value.find((x) => x.name === name)?.value ?? ''; + // Transforming the fetched data into a Configs object currentConfigs.value = { providers: { @@ -125,37 +142,31 @@ export function useConfigHandler(updatedConfigs?: Config) { name: 'google', enabled: allowedAuthProviders.value.includes('GOOGLE'), fields: { - client_id: - infraConfigs.value.find((x) => x.name === 'GOOGLE_CLIENT_ID') - ?.value ?? '', - client_secret: - infraConfigs.value.find((x) => x.name === 'GOOGLE_CLIENT_SECRET') - ?.value ?? '', + client_id: getFieldValue('GOOGLE_CLIENT_ID'), + client_secret: getFieldValue('GOOGLE_CLIENT_SECRET'), + callback_url: getFieldValue('GOOGLE_CALLBACK_URL'), + scope: getFieldValue('GOOGLE_SCOPE'), }, }, github: { name: 'github', enabled: allowedAuthProviders.value.includes('GITHUB'), fields: { - client_id: - infraConfigs.value.find((x) => x.name === 'GITHUB_CLIENT_ID') - ?.value ?? '', - client_secret: - infraConfigs.value.find((x) => x.name === 'GITHUB_CLIENT_SECRET') - ?.value ?? '', + client_id: getFieldValue('GITHUB_CLIENT_ID'), + client_secret: getFieldValue('GITHUB_CLIENT_SECRET'), + callback_url: getFieldValue('GITHUB_CALLBACK_URL'), + scope: getFieldValue('GITHUB_SCOPE'), }, }, microsoft: { name: 'microsoft', enabled: allowedAuthProviders.value.includes('MICROSOFT'), fields: { - client_id: - infraConfigs.value.find((x) => x.name === 'MICROSOFT_CLIENT_ID') - ?.value ?? '', - client_secret: - infraConfigs.value.find( - (x) => x.name === 'MICROSOFT_CLIENT_SECRET' - )?.value ?? '', + client_id: getFieldValue('MICROSOFT_CLIENT_ID'), + client_secret: getFieldValue('MICROSOFT_CLIENT_SECRET'), + callback_url: getFieldValue('MICROSOFT_CALLBACK_URL'), + scope: getFieldValue('MICROSOFT_SCOPE'), + tenant: getFieldValue('MICROSOFT_TENANT'), }, }, }, @@ -163,12 +174,8 @@ export function useConfigHandler(updatedConfigs?: Config) { name: 'email', enabled: allowedAuthProviders.value.includes('EMAIL'), fields: { - mailer_smtp_url: - infraConfigs.value.find((x) => x.name === 'MAILER_SMTP_URL') - ?.value ?? '', - mailer_from_address: - infraConfigs.value.find((x) => x.name === 'MAILER_ADDRESS_FROM') - ?.value ?? '', + mailer_smtp_url: getFieldValue('MAILER_SMTP_URL'), + mailer_from_address: getFieldValue('MAILER_ADDRESS_FROM'), }, }, dataSharingConfigs: { @@ -184,7 +191,7 @@ export function useConfigHandler(updatedConfigs?: Config) { workingConfigs.value = cloneDeep(currentConfigs.value); }); - // Trasforming the working configs back into the format required by the mutations + // Transforming the working configs back into the format required by the mutations const updatedInfraConfigs = computed(() => { let config: UpdatedConfigs[] = [ { @@ -202,13 +209,23 @@ export function useConfigHandler(updatedConfigs?: Config) { { name: 'GOOGLE_CLIENT_SECRET', value: updatedConfigs?.providers.google.fields.client_secret ?? '', + }, + { + name: 'GOOGLE_CALLBACK_URL', + value: updatedConfigs?.providers.google.fields.callback_url ?? '', + }, + { + name: 'GOOGLE_SCOPE', + value: updatedConfigs?.providers.google.fields.scope ?? '', } ); } else { config = config.filter( (item) => item.name !== 'GOOGLE_CLIENT_ID' && - item.name !== 'GOOGLE_CLIENT_SECRET' + item.name !== 'GOOGLE_CLIENT_SECRET' && + item.name !== 'GOOGLE_CALLBACK_URL' && + item.name !== 'GOOGLE_SCOPE' ); } if (updatedConfigs?.providers.microsoft.enabled) { @@ -220,13 +237,28 @@ export function useConfigHandler(updatedConfigs?: Config) { { name: 'MICROSOFT_CLIENT_SECRET', value: updatedConfigs?.providers.microsoft.fields.client_secret ?? '', + }, + { + name: 'MICROSOFT_CALLBACK_URL', + value: updatedConfigs?.providers.microsoft.fields.callback_url ?? '', + }, + { + name: 'MICROSOFT_SCOPE', + value: updatedConfigs?.providers.microsoft.fields.scope ?? '', + }, + { + name: 'MICROSOFT_TENANT', + value: updatedConfigs?.providers.microsoft.fields.tenant ?? '', } ); } else { config = config.filter( (item) => item.name !== 'MICROSOFT_CLIENT_ID' && - item.name !== 'MICROSOFT_CLIENT_SECRET' + item.name !== 'MICROSOFT_CLIENT_SECRET' && + item.name !== 'MICROSOFT_CALLBACK_URL' && + item.name !== 'MICROSOFT_SCOPE' && + item.name !== 'MICROSOFT_TENANT' ); } @@ -239,13 +271,23 @@ export function useConfigHandler(updatedConfigs?: Config) { { name: 'GITHUB_CLIENT_SECRET', value: updatedConfigs?.providers.github.fields.client_secret ?? '', + }, + { + name: 'GITHUB_CALLBACK_URL', + value: updatedConfigs?.providers.github.fields.callback_url ?? '', + }, + { + name: 'GITHUB_SCOPE', + value: updatedConfigs?.providers.github.fields.scope ?? '', } ); } else { config = config.filter( (item) => item.name !== 'GITHUB_CLIENT_ID' && - item.name !== 'GITHUB_CLIENT_SECRET' + item.name !== 'GITHUB_CLIENT_SECRET' && + item.name !== 'GITHUB_CALLBACK_URL' && + item.name !== 'GITHUB_SCOPE' ); }