Fix OAuth UI issues and allow to set additional query parameters

This commit is contained in:
Jan Oberhauser 2020-02-08 16:14:28 -08:00
parent eb285ef711
commit 5594543ec8
6 changed files with 86 additions and 35 deletions

View file

@ -731,8 +731,6 @@ class App {
// Encrypt the data // Encrypt the data
const credentials = new Credentials(incomingData.name, incomingData.type, incomingData.nodesAccess); const credentials = new Credentials(incomingData.name, incomingData.type, incomingData.nodesAccess);
_.unset(incomingData.data, 'csrfSecret');
_.unset(incomingData.data, 'oauthTokenData');
credentials.setData(incomingData.data, encryptionKey); credentials.setData(incomingData.data, encryptionKey);
const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb; const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb;
@ -899,7 +897,14 @@ class App {
// Update the credentials in DB // Update the credentials in DB
await Db.collections.Credentials!.update(req.query.id, newCredentialsData); 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;
})); }));
// ---------------------------------------- // ----------------------------------------

View file

@ -157,6 +157,13 @@ export interface IBinaryDisplayData {
runIndex: number; runIndex: number;
} }
export interface ICredentialsCreatedEvent {
data: ICredentialsDecryptedResponse;
options: {
closeDialog: boolean,
};
}
export interface IStartRunData { export interface IStartRunData {
workflowData: IWorkflowData; workflowData: IWorkflowData;
startNodes?: string[]; startNodes?: string[];

View file

@ -31,7 +31,10 @@ import Vue from 'vue';
import { restApi } from '@/components/mixins/restApi'; import { restApi } from '@/components/mixins/restApi';
import { showMessage } from '@/components/mixins/showMessage'; import { showMessage } from '@/components/mixins/showMessage';
import CredentialsInput from '@/components/CredentialsInput.vue'; import CredentialsInput from '@/components/CredentialsInput.vue';
import { ICredentialsDecryptedResponse } from '@/Interface'; import {
ICredentialsCreatedEvent,
ICredentialsDecryptedResponse,
} from '@/Interface';
import { import {
ICredentialType, ICredentialType,
@ -185,27 +188,31 @@ export default mixins(
return credentialData; return credentialData;
}, },
credentialsCreated (data: ICredentialsDecryptedResponse): void { credentialsCreated (eventData: ICredentialsCreatedEvent): void {
this.$emit('credentialsCreated', data); this.$emit('credentialsCreated', eventData);
this.$showMessage({ this.$showMessage({
title: 'Credentials created', title: 'Credentials created',
message: `The credential "${data.name}" got created!`, message: `The credential "${eventData.data.name}" got created!`,
type: 'success', type: 'success',
}); });
if (eventData.options.closeDialog === true) {
this.closeDialog(); this.closeDialog();
}
}, },
credentialsUpdated (data: ICredentialsDecryptedResponse): void { credentialsUpdated (eventData: ICredentialsCreatedEvent): void {
this.$emit('credentialsUpdated', data); this.$emit('credentialsUpdated', eventData);
this.$showMessage({ this.$showMessage({
title: 'Credentials updated', title: 'Credentials updated',
message: `The credential "${data.name}" got updated!`, message: `The credential "${eventData.data.name}" got updated!`,
type: 'success', type: 'success',
}); });
if (eventData.options.closeDialog === true) {
this.closeDialog(); this.closeDialog();
}
}, },
closeDialog (): void { closeDialog (): void {
// Handle the close externally as the visible parameter is an external prop // Handle the close externally as the visible parameter is an external prop

View file

@ -79,10 +79,10 @@
</el-row> </el-row>
<div class="action-buttons"> <div class="action-buttons">
<el-button type="success" @click="updateCredentials" v-if="credentialData"> <el-button type="success" @click="updateCredentials(true)" v-if="credentialDataDynamic">
Save Save
</el-button> </el-button>
<el-button type="success" @click="createCredentials" v-else> <el-button type="success" @click="createCredentials(true)" v-else>
Create Create
</el-button> </el-button>
</div> </div>
@ -141,6 +141,7 @@ export default mixins(
credentialsName: 'The name the credentials should be saved as. Use a name<br />which makes it clear to what exactly they give access to.<br />For credentials of an Email account that could be the Email address itself.', credentialsName: 'The name the credentials should be saved as. Use a name<br />which makes it clear to what exactly they give access to.<br />For credentials of an Email account that could be the Email address itself.',
nodesWithAccess: 'The nodes which allowed to use this credentials.', nodesWithAccess: 'The nodes which allowed to use this credentials.',
}, },
credentialDataTemp: null as ICredentialsDecryptedResponse | null,
nodesAccess: [] as string[], nodesAccess: [] as string[],
name: '', name: '',
propertyValue: {} as ICredentialDataDecryptedObject, propertyValue: {} as ICredentialDataDecryptedObject,
@ -182,6 +183,13 @@ export default mixins(
return !this.credentialTypeData.__overwrittenProperties || !this.credentialTypeData.__overwrittenProperties.includes(propertyData.name); return !this.credentialTypeData.__overwrittenProperties || !this.credentialTypeData.__overwrittenProperties.includes(propertyData.name);
}); });
}, },
credentialDataDynamic (): ICredentialsDecryptedResponse | null {
if (this.credentialData) {
return this.credentialData;
}
return this.credentialDataTemp;
},
isOAuthType (): boolean { isOAuthType (): boolean {
return this.credentialTypeData.name === 'oAuth2Api' || (this.credentialTypeData.extends !== undefined && this.credentialTypeData.extends.includes('oAuth2Api')); return this.credentialTypeData.name === 'oAuth2Api' || (this.credentialTypeData.extends !== undefined && this.credentialTypeData.extends.includes('oAuth2Api'));
}, },
@ -190,7 +198,7 @@ export default mixins(
return false; return false;
} }
return this.credentialData !== null && !!this.credentialData.data.oauthTokenData; return this.credentialDataDynamic !== null && !!this.credentialDataDynamic.data!.oauthTokenData;
}, },
}, },
methods: { methods: {
@ -202,7 +210,7 @@ export default mixins(
tempValue[name] = parameterData.value; tempValue[name] = parameterData.value;
Vue.set(this, 'propertyValue', tempValue); Vue.set(this, 'propertyValue', tempValue);
}, },
async createCredentials (doNotEmitData?: boolean): Promise<ICredentialsResponse | null> { async createCredentials (closeDialog: boolean): Promise<ICredentialsResponse | null> {
const nodesAccess = this.nodesAccess.map((nodeType) => { const nodesAccess = this.nodesAccess.map((nodeType) => {
return { return {
nodeType, nodeType,
@ -227,29 +235,30 @@ export default mixins(
// Add also to local store // Add also to local store
this.$store.commit('addCredentials', result); this.$store.commit('addCredentials', result);
if (doNotEmitData !== true) { this.$emit('credentialsCreated', {data: result, options: { closeDialog }});
this.$emit('credentialsCreated', result);
}
return result; return result;
}, },
async oAuth2CredentialAuthorize () { async oAuth2CredentialAuthorize () {
let url; let url;
let credentialData = this.credentialData; let credentialData = this.credentialDataDynamic;
let newCredentials = false; let newCredentials = false;
if (!credentialData) { if (!credentialData) {
// Credentials did not get created yet. So create first before // Credentials did not get created yet. So create first before
// doing oauth authorize // doing oauth authorize
credentialData = await this.createCredentials(true); credentialData = await this.createCredentials(false) as ICredentialsDecryptedResponse;
newCredentials = true; newCredentials = true;
if (credentialData === null) { if (credentialData === null) {
return; return;
} }
} else {
// Exists already but got maybe changed. So save first
credentialData = await this.updateCredentials(false) as ICredentialsDecryptedResponse;
} }
try { try {
url = await this.restApi().oAuth2CredentialAuthorize(credentialData) as string; url = await this.restApi().oAuth2CredentialAuthorize(credentialData as ICredentialsResponse) as string;
} catch (error) { } catch (error) {
this.$showError(error, 'OAuth Authorization Error', 'Error generating authorization URL:'); this.$showError(error, 'OAuth Authorization Error', 'Error generating authorization URL:');
return; return;
@ -268,7 +277,17 @@ export default mixins(
// Set some kind of data that status changes. // Set some kind of data that status changes.
// As data does not get displayed directly it does not matter what data. // 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 // Close the window
if (oauthPopup) { if (oauthPopup) {
@ -276,7 +295,7 @@ export default mixins(
} }
if (newCredentials === true) { if (newCredentials === true) {
this.$emit('credentialsCreated', credentialData); this.$emit('credentialsCreated', {data: credentialData, options: { closeDialog: false }});
} }
this.$showMessage({ this.$showMessage({
@ -292,13 +311,13 @@ export default mixins(
window.addEventListener('message', receiveMessage, false); window.addEventListener('message', receiveMessage, false);
}, },
async updateCredentials () { async updateCredentials (closeDialog: boolean): Promise<ICredentialsResponse> {
const nodesAccess: ICredentialNodeAccess[] = []; const nodesAccess: ICredentialNodeAccess[] = [];
const addedNodeTypes: string[] = []; const addedNodeTypes: string[] = [];
// Add Node-type which already had access to keep the original added date // Add Node-type which already had access to keep the original added date
let nodeAccessData: ICredentialNodeAccess; 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))) { if (this.nodesAccess.includes((nodeAccessData.nodeType))) {
nodesAccess.push(nodeAccessData); nodesAccess.push(nodeAccessData);
addedNodeTypes.push(nodeAccessData.nodeType); addedNodeTypes.push(nodeAccessData.nodeType);
@ -323,7 +342,7 @@ export default mixins(
let result; let result;
try { 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) { } catch (error) {
this.$showError(error, 'Problem Updating Credentials', 'There was a problem updating the credentials:'); this.$showError(error, 'Problem Updating Credentials', 'There was a problem updating the credentials:');
return; return;
@ -336,7 +355,9 @@ export default mixins(
// which have now a different name // which have now a different name
this.updateNodesCredentialsIssues(); this.updateNodesCredentialsIssues();
this.$emit('credentialsUpdated', result); this.$emit('credentialsUpdated', {data: result, options: { closeDialog }});
return result;
}, },
init () { init () {
if (this.credentialData) { if (this.credentialData) {

View file

@ -45,6 +45,7 @@ import Vue from 'vue';
import { restApi } from '@/components/mixins/restApi'; import { restApi } from '@/components/mixins/restApi';
import { import {
ICredentialsCreatedEvent,
ICredentialsResponse, ICredentialsResponse,
INodeUi, INodeUi,
INodeUpdatePropertiesInformation, INodeUpdatePropertiesInformation,
@ -134,21 +135,23 @@ export default mixins(
closeCredentialNewDialog () { closeCredentialNewDialog () {
this.credentialNewDialogVisible = false; this.credentialNewDialogVisible = false;
}, },
async credentialsCreated (data: ICredentialsResponse) { async credentialsCreated (eventData: ICredentialsCreatedEvent) {
await this.credentialsUpdated(data); await this.credentialsUpdated(eventData.data as ICredentialsResponse);
}, },
credentialsUpdated (data: ICredentialsResponse) { credentialsUpdated (eventData: ICredentialsCreatedEvent) {
if (!this.credentialTypesNode.includes(data.type)) { if (!this.credentialTypesNode.includes(eventData.data.type)) {
return; return;
} }
this.init(); 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 // 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);
if (eventData.options.closeDialog === true) {
this.closeCredentialNewDialog(); this.closeCredentialNewDialog();
}
}, },
credentialInputWrapperStyle (credentialType: string) { credentialInputWrapperStyle (credentialType: string) {
let deductWidth = 0; let deductWidth = 0;

View file

@ -45,5 +45,13 @@ export class OAuth2Api implements ICredentialType {
type: 'string' as NodePropertyTypes, type: 'string' as NodePropertyTypes,
default: '', 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',
},
]; ];
} }