From 56176bf6b577271933fe8ad430ee29ece7e61901 Mon Sep 17 00:00:00 2001 From: Sandra Zollner Date: Tue, 10 Mar 2026 11:25:02 +0100 Subject: [PATCH] feat(editor): Allow instance admin to re-enable disabled secret provider connections (#26760) --- .../frontend/@n8n/i18n/src/locales/en.json | 6 + .../src/api/secretsProvider.ee.ts | 14 +++ .../SecretsProviderConnectionCard.ee.vue | 28 ++++- .../SecretsProviderConnectionCard.test.ts | 103 ++++++++++++++++++ .../useSecretsProviderConnection.ee.ts | 6 + .../views/SettingsSecretsProviders.ee.vue | 26 +++++ .../views/SettingsSecretsProviders.test.ts | 77 +++++++++++++ 7 files changed, 257 insertions(+), 3 deletions(-) diff --git a/packages/frontend/@n8n/i18n/src/locales/en.json b/packages/frontend/@n8n/i18n/src/locales/en.json index 7d20e57bc07..800afadcdf7 100644 --- a/packages/frontend/@n8n/i18n/src/locales/en.json +++ b/packages/frontend/@n8n/i18n/src/locales/en.json @@ -37,6 +37,7 @@ "clientSecret": "Client Secret" } }, + "generic.activate": "Activate", "generic.annotations": "Annotations", "generic.annotationData": "Highlighted data", "generic.any": "Any", @@ -3088,6 +3089,10 @@ "settings.secretsProviderConnections.description": "Manage credentials across multiple environments by connecting an external secrets store. Keep sensitive credential information in your vault for added security.", "settings.secretsProviderConnections.card.createdAt": "Created on {date}", "settings.secretsProviderConnections.actions.share": "Share", + "settings.secretsProviderConnections.actions.activate.success.title": "Secret store active", + "settings.secretsProviderConnections.actions.activate.success.description": "\"{provider}\" is now active.", + "settings.secretsProviderConnections.actions.activate.error.title": "Couldn't activate secret store", + "settings.secretsProviderConnections.actions.activate.error.description": "Try again to make \"{provider}\" active.", "settings.secretsProviderConnections.oneSecret": "1 secret", "settings.secretsProviderConnections.secrets": "{count} secrets", "settings.secretsProviderConnections.modal.items.connection": "Connection", @@ -3113,6 +3118,7 @@ "settings.secretsProviderConnections.modal.connectionName.hint": "Enter a unique name for your secret store. This can not be changed later.", "settings.secretsProviderConnections.modal.connectionName.unique": "A secret store with this name already exists. Enter a unique name for your secret store.", "settings.secretsProviderConnections.state.disconnected": "Disconnected", + "settings.secretsProviderConnections.state.disabled": "Inactive", "settings.secretsProviderConnections.delete.title": "Delete \"{name}\"", "settings.secretsProviderConnections.delete.description": "Deleting this vault will remove it and all {secretsCount} imported from it. This will impact credentials that rely on those secrets.", "settings.secretsProviderConnections.delete.impact.title": "Impact:", diff --git a/packages/frontend/@n8n/rest-api-client/src/api/secretsProvider.ee.ts b/packages/frontend/@n8n/rest-api-client/src/api/secretsProvider.ee.ts index 179ad4e3161..8451880cfac 100644 --- a/packages/frontend/@n8n/rest-api-client/src/api/secretsProvider.ee.ts +++ b/packages/frontend/@n8n/rest-api-client/src/api/secretsProvider.ee.ts @@ -57,6 +57,20 @@ export const updateSecretProviderConnection = async ( ); }; +export const enableSecretProviderConnection = async ( + context: IRestApiContext, + providerKey: string, +): Promise => { + return await makeRestApiRequest( + context, + 'PATCH', + `/secret-providers/connections/${providerKey}`, + { + isEnabled: true, + }, + ); +}; + export const testSecretProviderConnection = async ( context: IRestApiContext, providerKey: string, diff --git a/packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/components/SecretsProviderConnectionCard.ee.vue b/packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/components/SecretsProviderConnectionCard.ee.vue index 9d373e77dc9..55da5768541 100644 --- a/packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/components/SecretsProviderConnectionCard.ee.vue +++ b/packages/frontend/editor-ui/src/features/integrations/secretsProviders.ee/components/SecretsProviderConnectionCard.ee.vue @@ -40,6 +40,7 @@ const emit = defineEmits<{ share: [providerKey: string]; reload: [providerKey: string]; delete: [providerKey: string]; + activate: [providerKey: string]; }>(); const provider = toRef(props, 'provider'); @@ -57,6 +58,8 @@ const showDisconnectedBadge = computed(() => { return provider.value.state === 'error'; }); +const isDisabled = computed(() => provider.value.isEnabled === false); + const canDelete = computed(() => { if (rbacStore.hasScope('externalSecretsProvider:delete')) return true; if (provider.value.projects.length > 0) { @@ -118,6 +121,14 @@ const actionDropdownOptions = computed(() => { value: 'edit', }, ]; + + if (isDisabled.value) { + options.push({ + label: i18n.baseText('generic.activate'), + value: 'activate', + }); + } + if (isProjectScopedSecretsEnabled) { options.push({ label: i18n.baseText('settings.secretsProviderConnections.actions.share'), @@ -125,7 +136,7 @@ const actionDropdownOptions = computed(() => { }); } - if (provider.value.state === 'connected' && canSync.value) { + if (provider.value.state === 'connected' && canSync.value && !isDisabled.value) { options.push({ label: i18n.baseText('settings.externalSecrets.card.actionDropdown.reload'), value: 'reload', @@ -145,6 +156,8 @@ const actionDropdownOptions = computed(() => { function onAction(action: string) { if (action === 'edit') { emit('edit', provider.value.name); + } else if (action === 'activate') { + emit('activate', provider.value.name); } else if (action === 'share') { emit('share', provider.value.name); } else if (action === 'reload') { @@ -170,7 +183,16 @@ function onAction(action: string) { provider.name }} + {{ i18n.baseText('settings.secretsProviderConnections.state.disabled') }} + +