From 5594543ec82fbbb1551abce1738a9e04235edcec Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sat, 8 Feb 2020 16:14:28 -0800 Subject: [PATCH] :zap: Fix OAuth UI issues and allow to set additional query parameters --- packages/cli/src/Server.ts | 11 ++-- packages/editor-ui/src/Interface.ts | 7 +++ .../src/components/CredentialsEdit.vue | 25 +++++---- .../src/components/CredentialsInput.vue | 53 +++++++++++++------ .../src/components/NodeCredentials.vue | 17 +++--- .../credentials/OAuth2Api.credentials.ts | 8 +++ 6 files changed, 86 insertions(+), 35 deletions(-) diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 9da615f7a3..1d82c4d807 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -731,8 +731,6 @@ class App { // Encrypt the data const credentials = new Credentials(incomingData.name, incomingData.type, incomingData.nodesAccess); - _.unset(incomingData.data, 'csrfSecret'); - _.unset(incomingData.data, 'oauthTokenData'); credentials.setData(incomingData.data, encryptionKey); const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb; @@ -899,7 +897,14 @@ class App { // Update the credentials in DB await Db.collections.Credentials!.update(req.query.id, newCredentialsData); - return oAuthObj.code.getUri(); + const authQueryParameters = _.get(oauthCredentials, 'authQueryParameters', '') as string; + let returnUri = oAuthObj.code.getUri(); + + if (authQueryParameters) { + returnUri += '&' + authQueryParameters; + } + + return returnUri; })); // ---------------------------------------- diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index 7c692881a8..e0e6355359 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -157,6 +157,13 @@ export interface IBinaryDisplayData { runIndex: number; } +export interface ICredentialsCreatedEvent { + data: ICredentialsDecryptedResponse; + options: { + closeDialog: boolean, + }; +} + export interface IStartRunData { workflowData: IWorkflowData; startNodes?: string[]; diff --git a/packages/editor-ui/src/components/CredentialsEdit.vue b/packages/editor-ui/src/components/CredentialsEdit.vue index 256cf70311..cd3b6c1f74 100644 --- a/packages/editor-ui/src/components/CredentialsEdit.vue +++ b/packages/editor-ui/src/components/CredentialsEdit.vue @@ -31,7 +31,10 @@ import Vue from 'vue'; import { restApi } from '@/components/mixins/restApi'; import { showMessage } from '@/components/mixins/showMessage'; import CredentialsInput from '@/components/CredentialsInput.vue'; -import { ICredentialsDecryptedResponse } from '@/Interface'; +import { + ICredentialsCreatedEvent, + ICredentialsDecryptedResponse, +} from '@/Interface'; import { ICredentialType, @@ -185,27 +188,31 @@ export default mixins( return credentialData; }, - credentialsCreated (data: ICredentialsDecryptedResponse): void { - this.$emit('credentialsCreated', data); + credentialsCreated (eventData: ICredentialsCreatedEvent): void { + this.$emit('credentialsCreated', eventData); this.$showMessage({ title: 'Credentials created', - message: `The credential "${data.name}" got created!`, + message: `The credential "${eventData.data.name}" got created!`, type: 'success', }); - this.closeDialog(); + if (eventData.options.closeDialog === true) { + this.closeDialog(); + } }, - credentialsUpdated (data: ICredentialsDecryptedResponse): void { - this.$emit('credentialsUpdated', data); + credentialsUpdated (eventData: ICredentialsCreatedEvent): void { + this.$emit('credentialsUpdated', eventData); this.$showMessage({ title: 'Credentials updated', - message: `The credential "${data.name}" got updated!`, + message: `The credential "${eventData.data.name}" got updated!`, type: 'success', }); - this.closeDialog(); + if (eventData.options.closeDialog === true) { + this.closeDialog(); + } }, closeDialog (): void { // Handle the close externally as the visible parameter is an external prop diff --git a/packages/editor-ui/src/components/CredentialsInput.vue b/packages/editor-ui/src/components/CredentialsInput.vue index d8be6d09a9..bc5b6225d3 100644 --- a/packages/editor-ui/src/components/CredentialsInput.vue +++ b/packages/editor-ui/src/components/CredentialsInput.vue @@ -79,10 +79,10 @@
- + Save - + Create
@@ -141,6 +141,7 @@ export default mixins( credentialsName: 'The name the credentials should be saved as. Use a name
which makes it clear to what exactly they give access to.
For credentials of an Email account that could be the Email address itself.', nodesWithAccess: 'The nodes which allowed to use this credentials.', }, + credentialDataTemp: null as ICredentialsDecryptedResponse | null, nodesAccess: [] as string[], name: '', propertyValue: {} as ICredentialDataDecryptedObject, @@ -182,6 +183,13 @@ export default mixins( return !this.credentialTypeData.__overwrittenProperties || !this.credentialTypeData.__overwrittenProperties.includes(propertyData.name); }); }, + credentialDataDynamic (): ICredentialsDecryptedResponse | null { + if (this.credentialData) { + return this.credentialData; + } + + return this.credentialDataTemp; + }, isOAuthType (): boolean { return this.credentialTypeData.name === 'oAuth2Api' || (this.credentialTypeData.extends !== undefined && this.credentialTypeData.extends.includes('oAuth2Api')); }, @@ -190,7 +198,7 @@ export default mixins( return false; } - return this.credentialData !== null && !!this.credentialData.data.oauthTokenData; + return this.credentialDataDynamic !== null && !!this.credentialDataDynamic.data!.oauthTokenData; }, }, methods: { @@ -202,7 +210,7 @@ export default mixins( tempValue[name] = parameterData.value; Vue.set(this, 'propertyValue', tempValue); }, - async createCredentials (doNotEmitData?: boolean): Promise { + async createCredentials (closeDialog: boolean): Promise { const nodesAccess = this.nodesAccess.map((nodeType) => { return { nodeType, @@ -227,29 +235,30 @@ export default mixins( // Add also to local store this.$store.commit('addCredentials', result); - if (doNotEmitData !== true) { - this.$emit('credentialsCreated', result); - } + this.$emit('credentialsCreated', {data: result, options: { closeDialog }}); return result; }, async oAuth2CredentialAuthorize () { let url; - let credentialData = this.credentialData; + let credentialData = this.credentialDataDynamic; let newCredentials = false; if (!credentialData) { // Credentials did not get created yet. So create first before // doing oauth authorize - credentialData = await this.createCredentials(true); + credentialData = await this.createCredentials(false) as ICredentialsDecryptedResponse; newCredentials = true; if (credentialData === null) { return; } + } else { + // Exists already but got maybe changed. So save first + credentialData = await this.updateCredentials(false) as ICredentialsDecryptedResponse; } try { - url = await this.restApi().oAuth2CredentialAuthorize(credentialData) as string; + url = await this.restApi().oAuth2CredentialAuthorize(credentialData as ICredentialsResponse) as string; } catch (error) { this.$showError(error, 'OAuth Authorization Error', 'Error generating authorization URL:'); return; @@ -268,7 +277,17 @@ export default mixins( // Set some kind of data that status changes. // As data does not get displayed directly it does not matter what data. - credentialData.data.oauthTokenData = {}; + if (this.credentialData === null) { + // Are new credentials so did not get send via "credentialData" + this.credentialDataTemp = credentialData as ICredentialsDecryptedResponse; + Vue.set(this.credentialDataTemp.data, 'oauthTokenData', {}); + } else { + // Credentials did already exist so can be set directly + Vue.set(this.credentialData.data, 'oauthTokenData', {}); + } + + // Save that OAuth got authorized locally + this.$store.commit('updateCredentials', this.credentialDataDynamic); // Close the window if (oauthPopup) { @@ -276,7 +295,7 @@ export default mixins( } if (newCredentials === true) { - this.$emit('credentialsCreated', credentialData); + this.$emit('credentialsCreated', {data: credentialData, options: { closeDialog: false }}); } this.$showMessage({ @@ -292,13 +311,13 @@ export default mixins( window.addEventListener('message', receiveMessage, false); }, - async updateCredentials () { + async updateCredentials (closeDialog: boolean): Promise { const nodesAccess: ICredentialNodeAccess[] = []; const addedNodeTypes: string[] = []; // Add Node-type which already had access to keep the original added date let nodeAccessData: ICredentialNodeAccess; - for (nodeAccessData of (this.credentialData as ICredentialsDecryptedResponse).nodesAccess) { + for (nodeAccessData of (this.credentialDataDynamic as ICredentialsDecryptedResponse).nodesAccess) { if (this.nodesAccess.includes((nodeAccessData.nodeType))) { nodesAccess.push(nodeAccessData); addedNodeTypes.push(nodeAccessData.nodeType); @@ -323,7 +342,7 @@ export default mixins( let result; try { - result = await this.restApi().updateCredentials((this.credentialData as ICredentialsDecryptedResponse).id as string, newCredentials); + result = await this.restApi().updateCredentials((this.credentialDataDynamic as ICredentialsDecryptedResponse).id as string, newCredentials); } catch (error) { this.$showError(error, 'Problem Updating Credentials', 'There was a problem updating the credentials:'); return; @@ -336,7 +355,9 @@ export default mixins( // which have now a different name this.updateNodesCredentialsIssues(); - this.$emit('credentialsUpdated', result); + this.$emit('credentialsUpdated', {data: result, options: { closeDialog }}); + + return result; }, init () { if (this.credentialData) { diff --git a/packages/editor-ui/src/components/NodeCredentials.vue b/packages/editor-ui/src/components/NodeCredentials.vue index 213f142840..8284c272e1 100644 --- a/packages/editor-ui/src/components/NodeCredentials.vue +++ b/packages/editor-ui/src/components/NodeCredentials.vue @@ -45,6 +45,7 @@ import Vue from 'vue'; import { restApi } from '@/components/mixins/restApi'; import { + ICredentialsCreatedEvent, ICredentialsResponse, INodeUi, INodeUpdatePropertiesInformation, @@ -134,21 +135,23 @@ export default mixins( closeCredentialNewDialog () { this.credentialNewDialogVisible = false; }, - async credentialsCreated (data: ICredentialsResponse) { - await this.credentialsUpdated(data); + async credentialsCreated (eventData: ICredentialsCreatedEvent) { + await this.credentialsUpdated(eventData.data as ICredentialsResponse); }, - credentialsUpdated (data: ICredentialsResponse) { - if (!this.credentialTypesNode.includes(data.type)) { + credentialsUpdated (eventData: ICredentialsCreatedEvent) { + if (!this.credentialTypesNode.includes(eventData.data.type)) { return; } this.init(); - Vue.set(this.credentials, data.type, data.name); + Vue.set(this.credentials, eventData.data.type, eventData.data.name); // Makes sure that it does also get set correctly on the node not just the UI - this.credentialSelected(data.type); + this.credentialSelected(eventData.data.type); - this.closeCredentialNewDialog(); + if (eventData.options.closeDialog === true) { + this.closeCredentialNewDialog(); + } }, credentialInputWrapperStyle (credentialType: string) { let deductWidth = 0; diff --git a/packages/nodes-base/credentials/OAuth2Api.credentials.ts b/packages/nodes-base/credentials/OAuth2Api.credentials.ts index 99e8e025de..efa5127e6e 100644 --- a/packages/nodes-base/credentials/OAuth2Api.credentials.ts +++ b/packages/nodes-base/credentials/OAuth2Api.credentials.ts @@ -45,5 +45,13 @@ export class OAuth2Api implements ICredentialType { type: 'string' as NodePropertyTypes, default: '', }, + { + displayName: 'Auth URI Query Parameters', + name: 'authQueryParameters', + type: 'string' as NodePropertyTypes, + default: '', + description: 'For some services additional query parameters have to be set which can be defined here.', + placeholder: 'access_type=offline', + }, ]; }