diff --git a/lerna.json b/lerna.json index 39a4de05fa..5f01f98b3a 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.3.18-alpha.14", + "version": "2.3.18-alpha.17", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 658641bb1b..db49070128 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "2.3.18-alpha.14", + "version": "2.3.18-alpha.17", "description": "Budibase backend core libraries used in server and worker", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", @@ -24,7 +24,7 @@ "dependencies": { "@budibase/nano": "10.1.1", "@budibase/pouchdb-replication-stream": "1.2.10", - "@budibase/types": "2.3.18-alpha.14", + "@budibase/types": "2.3.18-alpha.17", "@shopify/jest-koa-mocks": "5.0.1", "@techpass/passport-openidconnect": "0.3.2", "aws-cloudfront-sign": "2.2.0", diff --git a/packages/backend-core/src/configs/configs.ts b/packages/backend-core/src/configs/configs.ts index 882c37ceb9..38e7bcbb29 100644 --- a/packages/backend-core/src/configs/configs.ts +++ b/packages/backend-core/src/configs/configs.ts @@ -154,11 +154,29 @@ export async function getGoogleConfig(): Promise< GoogleInnerConfig | undefined > { const config = await getGoogleConfigDoc() - if (config) { - return config.config + return config?.config +} + +export async function getGoogleDatasourceConfig(): Promise< + GoogleInnerConfig | undefined +> { + if (!env.SELF_HOSTED) { + // always use the env vars in cloud + return getDefaultGoogleConfig() } - // Use google fallback configuration from env variables + // prefer the config in self-host + let config = await getGoogleConfig() + + // fallback to env vars + if (!config || !config.activated) { + config = getDefaultGoogleConfig() + } + + return config +} + +export function getDefaultGoogleConfig(): GoogleInnerConfig | undefined { if (environment.GOOGLE_CLIENT_ID && environment.GOOGLE_CLIENT_SECRET) { return { clientID: environment.GOOGLE_CLIENT_ID!, diff --git a/packages/backend-core/src/middleware/passport/datasource/google.ts b/packages/backend-core/src/middleware/passport/datasource/google.ts index ff5224e437..32451cb8d2 100644 --- a/packages/backend-core/src/middleware/passport/datasource/google.ts +++ b/packages/backend-core/src/middleware/passport/datasource/google.ts @@ -12,7 +12,8 @@ type Passport = { } async function fetchGoogleCreds() { - const config = await configs.getGoogleConfig() + let config = await configs.getGoogleDatasourceConfig() + if (!config) { throw new Error("No google configuration found") } diff --git a/packages/backend-core/src/users.ts b/packages/backend-core/src/users.ts index 9ef2c5c31f..8963f7c141 100644 --- a/packages/backend-core/src/users.ts +++ b/packages/backend-core/src/users.ts @@ -5,6 +5,7 @@ import { generateAppUserID, queryGlobalView, UNICODE_MAX, + directCouchFind, } from "./db" import { BulkDocsResponse, User } from "@budibase/types" import { getGlobalDB } from "./context" @@ -101,6 +102,7 @@ export const searchGlobalUsersByApp = async ( }) params.startkey = opts && opts.startkey ? opts.startkey : params.startkey let response = await queryGlobalView(ViewName.USER_BY_APP, params) + if (!response) { response = [] } @@ -111,6 +113,45 @@ export const searchGlobalUsersByApp = async ( return users } +/* + Return any user who potentially has access to the application + Admins, developers and app users with the explicitly role. +*/ +export const searchGlobalUsersByAppAccess = async (appId: any, opts: any) => { + const roleSelector = `roles.${appId}` + + let orQuery: any[] = [ + { + "builder.global": true, + }, + { + "admin.global": true, + }, + ] + + if (appId) { + const roleCheck = { + [roleSelector]: { + $exists: true, + }, + } + orQuery.push(roleCheck) + } + + let searchOptions = { + selector: { + $or: orQuery, + _id: { + $regex: "^us_", + }, + }, + limit: opts?.limit || 50, + } + + const resp = await directCouchFind(context.getGlobalDBName(), searchOptions) + return resp?.rows +} + export const getGlobalUserByAppPage = (appId: string, user: User) => { if (!user) { return diff --git a/packages/bbui/package.json b/packages/bbui/package.json index ebf2790cb5..c200860933 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "2.3.18-alpha.14", + "version": "2.3.18-alpha.17", "license": "MPL-2.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", @@ -38,7 +38,7 @@ ], "dependencies": { "@adobe/spectrum-css-workflow-icons": "1.2.1", - "@budibase/string-templates": "2.3.18-alpha.14", + "@budibase/string-templates": "2.3.18-alpha.17", "@spectrum-css/accordion": "3.0.24", "@spectrum-css/actionbutton": "1.0.1", "@spectrum-css/actiongroup": "1.0.1", diff --git a/packages/bbui/src/ActionButton/ActionButton.svelte b/packages/bbui/src/ActionButton/ActionButton.svelte index 663128160f..01f5033e6c 100644 --- a/packages/bbui/src/ActionButton/ActionButton.svelte +++ b/packages/bbui/src/ActionButton/ActionButton.svelte @@ -1,6 +1,9 @@ - + + diff --git a/packages/bbui/src/Form/Core/Picker.svelte b/packages/bbui/src/Form/Core/Picker.svelte index 5cef0f9213..104596c8cb 100644 --- a/packages/bbui/src/Form/Core/Picker.svelte +++ b/packages/bbui/src/Form/Core/Picker.svelte @@ -24,6 +24,7 @@ export let getOptionLabel = option => option export let getOptionValue = option => option export let getOptionIcon = () => null + export let useOptionIconImage = false export let getOptionColour = () => null export let open = false export let readonly = false @@ -33,6 +34,9 @@ export let sort = false export let fetchTerm = null export let customPopoverHeight + export let align = "left" + export let footer = null + const dispatch = createEventDispatcher() let searchTerm = null @@ -131,7 +135,7 @@ (open = false)} @@ -186,7 +190,16 @@ > {#if getOptionIcon(option, idx)} - + {#if useOptionIconImage} + icon + {:else} + + {/if} {/if} {#if getOptionColour(option, idx)} @@ -208,6 +221,12 @@ {/each} {/if} + + {#if footer} + + {/if} @@ -284,4 +303,11 @@ .popover-content :global(.spectrum-Search .spectrum-Textfield-icon) { top: 9px; } + + .footer { + padding: 4px 12px 12px 12px; + font-style: italic; + max-width: 170px; + font-size: 12px; + } diff --git a/packages/bbui/src/Form/Core/Select.svelte b/packages/bbui/src/Form/Core/Select.svelte index 721083e3a6..af45c1d9ff 100644 --- a/packages/bbui/src/Form/Core/Select.svelte +++ b/packages/bbui/src/Form/Core/Select.svelte @@ -11,6 +11,7 @@ export let getOptionLabel = option => option export let getOptionValue = option => option export let getOptionIcon = () => null + export let useOptionIconImage = false export let getOptionColour = () => null export let isOptionEnabled export let readonly = false @@ -18,6 +19,8 @@ export let autoWidth = false export let autocomplete = false export let sort = false + export let align + export let footer = null const dispatch = createEventDispatcher() @@ -41,7 +44,7 @@ const getFieldText = (value, options, placeholder) => { // Always use placeholder if no value if (value == null || value === "") { - return placeholder || "Choose an option" + return placeholder !== false ? "Choose an option" : "" } return getFieldAttribute(getOptionLabel, value, options) @@ -66,15 +69,18 @@ {fieldColour} {options} {autoWidth} + {align} + {footer} {getOptionLabel} {getOptionValue} {getOptionIcon} + {useOptionIconImage} {getOptionColour} {isOptionEnabled} {autocomplete} {sort} isPlaceholder={value == null || value === ""} - placeholderOption={placeholder} + placeholderOption={placeholder === false ? null : placeholder} isOptionSelected={option => option === value} onSelectOption={selectOption} /> diff --git a/packages/bbui/src/Form/Select.svelte b/packages/bbui/src/Form/Select.svelte index 76fe613c92..e87496652d 100644 --- a/packages/bbui/src/Form/Select.svelte +++ b/packages/bbui/src/Form/Select.svelte @@ -14,6 +14,7 @@ export let getOptionLabel = option => extractProperty(option, "label") export let getOptionValue = option => extractProperty(option, "value") export let getOptionIcon = option => option?.icon + export let useOptionIconImage = false export let getOptionColour = option => option?.colour export let isOptionEnabled export let quiet = false @@ -22,6 +23,8 @@ export let tooltip = "" export let autocomplete = false export let customPopoverHeight + export let align + export let footer = null const dispatch = createEventDispatcher() const onChange = e => { @@ -48,10 +51,13 @@ {placeholder} {autoWidth} {sort} + {align} + {footer} {getOptionLabel} {getOptionValue} {getOptionIcon} {getOptionColour} + {useOptionIconImage} {isOptionEnabled} {autocomplete} {customPopoverHeight} diff --git a/packages/builder/package.json b/packages/builder/package.json index d5fc757ca3..a06235cbb5 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "2.3.18-alpha.14", + "version": "2.3.18-alpha.17", "license": "GPL-3.0", "private": true, "scripts": { @@ -58,10 +58,10 @@ } }, "dependencies": { - "@budibase/bbui": "2.3.18-alpha.14", - "@budibase/client": "2.3.18-alpha.14", - "@budibase/frontend-core": "2.3.18-alpha.14", - "@budibase/string-templates": "2.3.18-alpha.14", + "@budibase/bbui": "2.3.18-alpha.17", + "@budibase/client": "2.3.18-alpha.17", + "@budibase/frontend-core": "2.3.18-alpha.17", + "@budibase/string-templates": "2.3.18-alpha.17", "@fortawesome/fontawesome-svg-core": "^6.2.1", "@fortawesome/free-brands-svg-icons": "^6.2.1", "@fortawesome/free-solid-svg-icons": "^6.2.1", diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index d58a2d5b9e..51f88add27 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -72,6 +72,8 @@ const INITIAL_FRONTEND_STATE = { // onboarding onboarding: false, tourNodes: null, + + builderSidePanel: false, } export const getFrontendStore = () => { diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index d7225a6645..352f094507 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -192,13 +192,13 @@ editableColumn.name = originalName } - function deleteColumn() { + async function deleteColumn() { try { editableColumn.name = deleteColName if (editableColumn.name === $tables.selected.primaryDisplay) { notifications.error("You cannot delete the display column") } else { - tables.deleteField(editableColumn) + await tables.deleteField(editableColumn) notifications.success(`Column ${editableColumn.name} deleted.`) confirmDeleteDialog.hide() hide() diff --git a/packages/builder/src/components/common/RoleSelect.svelte b/packages/builder/src/components/common/RoleSelect.svelte index 645e82c8ba..09a67cb6fe 100644 --- a/packages/builder/src/components/common/RoleSelect.svelte +++ b/packages/builder/src/components/common/RoleSelect.svelte @@ -11,16 +11,24 @@ export let quiet = false export let allowPublic = true export let allowRemove = false + export let disabled = false + export let align + export let footer = null + export let allowedRoles = null const dispatch = createEventDispatcher() const RemoveID = "remove" - $: options = getOptions($roles, allowPublic, allowRemove) + $: options = getOptions($roles, allowPublic, allowRemove, allowedRoles) - const getOptions = (roles, allowPublic) => { + const getOptions = (roles, allowPublic, allowRemove, allowedRoles) => { + if (allowedRoles?.length) { + return roles.filter(role => allowedRoles.includes(role._id)) + } + let newRoles = [...roles] if (allowRemove) { - roles = [ - ...roles, + newRoles = [ + ...newRoles, { _id: RemoveID, name: "Remove", @@ -28,9 +36,9 @@ ] } if (allowPublic) { - return roles + return newRoles } - return roles.filter(role => role._id !== Constants.Roles.PUBLIC) + return newRoles.filter(role => role._id !== Constants.Roles.PUBLIC) } const getColor = role => { @@ -59,6 +67,9 @@ { + query = e.target.value.trim() + }} + on:focus={() => (searchFocus = true)} + on:blur={() => (searchFocus = false)} + /> + + + { + if (!query) { + return + } + query = null + userOnboardResponse = null + }} + > + + + + +
+ {#if promptInvite && !userOnboardResponse} + +
+ No user found +
+ Add a valid email to invite a new user +
+
+
+ {query || ""} + + Add user + +
+
+ {/if} + + {#if !promptInvite} + + {#if filteredInvites?.length} + +
+
Pending invites
+
Access
+
+ {#each filteredInvites as invite} +
+
+
+ {invite.email} +
+
+
+ { + onUpdateUserInvite(invite, e.detail) + }} + on:remove={() => { + onUninviteAppUser(invite) + }} + autoWidth + align="right" + /> +
+
+ {/each} +
+ {/if} + + {#if $licensing.groupsEnabled && filteredGroups?.length} + +
+
Groups
+
Access
+
+ {#each filteredGroups as group} +
{ + if (selectedGroup != group._id) { + selectedGroup = group._id + } else { + selectedGroup = null + } + }} + on:keydown={() => {}} + > +
+ +
+ {group.name} +
+
+ {`${group.users?.length} user${ + group.users?.length != 1 ? "s" : "" + }`} +
+
+
+ { + onUpdateGroup(group, e.detail) + }} + on:remove={() => { + onUpdateGroup(group) + }} + autoWidth + align="right" + /> +
+
+ {/each} +
+ {/if} + + {#if filteredUsers?.length} +
+
+
Users
+
Access
+
+ {#each allUsers as user} +
+
+
+ {user.email} +
+
+ {userTitle(user)} +
+
+
+ { + onUpdateUser(user, e.detail) + }} + on:remove={() => { + onUpdateUser(user) + }} + autoWidth + align="right" + allowedRoles={user.isBuilderOrAdmin + ? [Constants.Roles.ADMIN] + : null} + /> +
+
+ {/each} +
+ {/if} +
+ {/if} + + {#if userOnboardResponse?.created} + +
+ User added! +
+ Email invites are not available without SMTP configuration. Here is + the password that has been generated for this user. +
+
+
+ +
+
+ {/if} +
+ + + diff --git a/packages/builder/src/pages/builder/app/[application]/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/_layout.svelte index f561bd8ecd..a4b4982fca 100644 --- a/packages/builder/src/pages/builder/app/[application]/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/_layout.svelte @@ -13,15 +13,14 @@ notifications, } from "@budibase/bbui" - import RevertModal from "components/deploy/RevertModal.svelte" - import VersionModal from "components/deploy/VersionModal.svelte" - import DeployNavigation from "components/deploy/DeployNavigation.svelte" + import AppActions from "components/deploy/AppActions.svelte" import { API } from "api" import { isActive, goto, layout, redirect } from "@roxi/routify" import { capitalise } from "helpers" import { onMount, onDestroy } from "svelte" import TourWrap from "components/portal/onboarding/TourWrap.svelte" import TourPopover from "components/portal/onboarding/TourPopover.svelte" + import BuilderSidePanel from "./_components/BuilderSidePanel.svelte" import { TOUR_KEYS, TOURS } from "components/portal/onboarding/tours.js" export let application @@ -69,22 +68,32 @@ } const initTour = async () => { - if ( - !$auth.user?.onboardedAt && - isEnabled(TENANT_FEATURE_FLAGS.ONBOARDING_TOUR) - ) { - // Determine the correct step - const activeNav = $layout.children.find(c => $isActive(c.path)) - const onboardingTour = TOURS[TOUR_KEYS.TOUR_BUILDER_ONBOARDING] - const targetStep = activeNav - ? onboardingTour.find(step => step.route === activeNav?.path) - : null - await store.update(state => ({ - ...state, - onboarding: true, - tourKey: TOUR_KEYS.TOUR_BUILDER_ONBOARDING, - tourStepKey: targetStep?.id, - })) + // Check if onboarding is enabled. + if (isEnabled(TENANT_FEATURE_FLAGS.ONBOARDING_TOUR)) { + if (!$auth.user?.onboardedAt) { + // Determine the correct step + const activeNav = $layout.children.find(c => $isActive(c.path)) + const onboardingTour = TOURS[TOUR_KEYS.TOUR_BUILDER_ONBOARDING] + const targetStep = activeNav + ? onboardingTour.find(step => step.route === activeNav?.path) + : null + await store.update(state => ({ + ...state, + onboarding: true, + tourKey: TOUR_KEYS.TOUR_BUILDER_ONBOARDING, + tourStepKey: targetStep?.id, + })) + } else { + // Feature tour date + const release_date = new Date("2023-03-01T00:00:00.000Z") + const onboarded = new Date($auth.user?.onboardedAt) + if (onboarded < release_date) { + await store.update(state => ({ + ...state, + tourKey: TOUR_KEYS.FEATURE_ONBOARDING, + })) + } + } } } @@ -116,6 +125,11 @@
{:then _} + + {#if $store.builderSidePanel} + + {/if} +
@@ -181,11 +195,7 @@
-
- -
- - +
@@ -250,10 +260,6 @@ flex-direction: row; justify-content: flex-end; align-items: center; - gap: var(--spacing-xl); - } - - .version { - margin-right: var(--spacing-s); + gap: var(--spacing-l); } diff --git a/packages/builder/src/pages/builder/portal/settings/auth/index.svelte b/packages/builder/src/pages/builder/portal/settings/auth/index.svelte index e5196a1cbf..af272b5d7c 100644 --- a/packages/builder/src/pages/builder/portal/settings/auth/index.svelte +++ b/packages/builder/src/pages/builder/portal/settings/auth/index.svelte @@ -131,24 +131,25 @@ isEqual(providers.google?.config, originalGoogleDoc?.config) ? (googleSaveButtonDisabled = true) : (googleSaveButtonDisabled = false) + + // delete the callback url which is never saved to the oidc + // config doc, to ensure an accurate comparison + delete providers.oidc?.config.configs[0].callbackURL + isEqual(providers.oidc?.config, originalOidcDoc?.config) ? (oidcSaveButtonDisabled = true) : (oidcSaveButtonDisabled = false) } - // Create a flag so that it will only try to save completed forms - $: partialGoogle = - providers.google?.config?.clientID || providers.google?.config?.clientSecret - $: partialOidc = - providers.oidc?.config?.configs[0].configUrl || - providers.oidc?.config?.configs[0].clientID || - providers.oidc?.config?.configs[0].clientSecret - $: googleComplete = + $: googleComplete = !!( providers.google?.config?.clientID && providers.google?.config?.clientSecret - $: oidcComplete = + ) + + $: oidcComplete = !!( providers.oidc?.config?.configs[0].configUrl && providers.oidc?.config?.configs[0].clientID && providers.oidc?.config?.configs[0].clientSecret + ) const onFileSelected = e => { let fileName = e.target.files[0].name @@ -159,74 +160,88 @@ async function toggleIsSSOEnforced() { const value = $organisation.isSSOEnforced - await organisation.save({ isSSOEnforced: !value }) + try { + await organisation.save({ isSSOEnforced: !value }) + } catch (e) { + notifications.error(e.message) + } } - async function save(docs) { - let calls = [] - // Only if the user has provided an image, upload it + async function saveConfig(config) { + // Delete unsupported fields + delete config.createdAt + delete config.updatedAt + return API.saveConfig(config) + } + + async function saveOIDCLogo() { if (image) { let data = new FormData() data.append("file", image) - calls.push( - API.uploadOIDCLogo({ - name: image.name, - data, - }) + await API.uploadOIDCLogo({ + name: image.name, + data, + }) + } + } + + async function saveOIDC() { + if (!oidcComplete) { + notifications.error( + `Please fill in all required ${ConfigTypes.OIDC} fields` ) + return } - docs.forEach(element => { - // Delete unsupported fields - delete element.createdAt - delete element.updatedAt - const { activated } = element.config + const oidc = providers.oidc - if (element.type === ConfigTypes.OIDC) { - // Add a UUID here so each config is distinguishable when it arrives at the login page - for (let config of element.config.configs) { - if (!config.uuid) { - config.uuid = Helpers.uuid() - } - // Callback urls shouldn't be included - delete config.callbackURL - } - if ((partialOidc || activated) && !oidcComplete) { - notifications.error( - `Please fill in all required ${ConfigTypes.OIDC} fields` - ) - } else if (oidcComplete || !activated) { - calls.push(API.saveConfig(element)) - // Turn the save button grey when clicked - oidcSaveButtonDisabled = true - originalOidcDoc = cloneDeep(providers.oidc) - } + // Add a UUID here so each config is distinguishable when it arrives at the login page + for (let config of oidc.config.configs) { + if (!config.uuid) { + config.uuid = Helpers.uuid() } - if (element.type === ConfigTypes.Google) { - if ((partialGoogle || activated) && !googleComplete) { - notifications.error( - `Please fill in all required ${ConfigTypes.Google} fields` - ) - } else if (googleComplete || !activated) { - calls.push(API.saveConfig(element)) - googleSaveButtonDisabled = true - originalGoogleDoc = cloneDeep(providers.google) - } - } - }) - if (calls.length) { - Promise.all(calls) - .then(data => { - data.forEach(res => { - providers[res.type]._rev = res._rev - providers[res.type]._id = res._id - }) - notifications.success(`Settings saved`) - }) - .catch(() => { - notifications.error("Failed to update auth settings") - }) + // Callback urls shouldn't be included + delete config.callbackURL } + + try { + const res = await saveConfig(oidc) + providers[res.type]._rev = res._rev + providers[res.type]._id = res._id + await saveOIDCLogo() + notifications.success(`Settings saved`) + } catch (e) { + notifications.error(e.message) + return + } + + // Turn the save button grey when clicked + oidcSaveButtonDisabled = true + originalOidcDoc = cloneDeep(providers.oidc) + } + + async function saveGoogle() { + if (!googleComplete) { + notifications.error( + `Please fill in all required ${ConfigTypes.Google} fields` + ) + return + } + + const google = providers.google + + try { + const res = await saveConfig(google) + providers[res.type]._rev = res._rev + providers[res.type]._id = res._id + notifications.success(`Settings saved`) + } catch (e) { + notifications.error(e.message) + return + } + + googleSaveButtonDisabled = true + originalGoogleDoc = cloneDeep(providers.google) } let defaultScopes = ["profile", "email", "offline_access"] @@ -266,7 +281,7 @@ if (!googleDoc?._id) { providers.google = { type: ConfigTypes.Google, - config: { activated: true }, + config: { activated: false }, } originalGoogleDoc = cloneDeep(googleDoc) } else { @@ -290,14 +305,17 @@ } if (oidcLogos?.config) { const logoKeys = Object.keys(oidcLogos.config) - logoKeys.map(logoKey => { - const logoUrl = oidcLogos.config[logoKey] - iconDropdownOptions.unshift({ - label: logoKey, - value: logoKey, - icon: logoUrl, + logoKeys + // don't include the etag entry in the logo config + .filter(key => !key.toLowerCase().includes("etag")) + .map(logoKey => { + const logoUrl = oidcLogos.config[logoKey] + iconDropdownOptions.unshift({ + label: logoKey, + value: logoKey, + icon: logoUrl, + }) }) - }) } // Fetch OIDC config @@ -310,7 +328,7 @@ if (!oidcDoc?._id) { providers.oidc = { type: ConfigTypes.OIDC, - config: { configs: [{ activated: true, scopes: defaultScopes }] }, + config: { configs: [{ activated: false, scopes: defaultScopes }] }, } } else { originalOidcDoc = cloneDeep(oidcDoc) @@ -413,7 +431,7 @@ @@ -469,6 +487,7 @@