From cb1849db9dbfeb91d15a3203246068d5f3d54639 Mon Sep 17 00:00:00 2001 From: Rupenieks Date: Wed, 17 Jun 2020 19:38:30 +0200 Subject: [PATCH] NextCloud OAuth2 support --- .../NextCloudOAuth2Api.credentials.ts | 54 +++++++++++++++ .../nodes/NextCloud/GenericFunctions.ts | 64 ++++++++++++++++++ .../nodes/NextCloud/NextCloud.node.ts | 65 ++++++++++++++----- packages/nodes-base/package.json | 1 + 4 files changed, 167 insertions(+), 17 deletions(-) create mode 100644 packages/nodes-base/credentials/NextCloudOAuth2Api.credentials.ts create mode 100644 packages/nodes-base/nodes/NextCloud/GenericFunctions.ts diff --git a/packages/nodes-base/credentials/NextCloudOAuth2Api.credentials.ts b/packages/nodes-base/credentials/NextCloudOAuth2Api.credentials.ts new file mode 100644 index 000000000..dbb4efcda --- /dev/null +++ b/packages/nodes-base/credentials/NextCloudOAuth2Api.credentials.ts @@ -0,0 +1,54 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + + +export class NextCloudOAuth2Api implements ICredentialType { + name = 'nextCloudOAuth2Api'; + extends = [ + 'oAuth2Api', + ]; + displayName = 'NextCloud OAuth2 API'; + properties = [ + { + displayName: 'Web DAV URL', + name: 'webDavUrl', + type: 'string' as NodePropertyTypes, + placeholder: 'https://nextcloud.example.com/remote.php/webdav/', + default: '', + }, + { + displayName: 'Authorization URL', + name: 'authUrl', + type: 'string' as NodePropertyTypes, + default: 'https://nextcloud.example.com/apps/oauth2/authorize', + required: true, + }, + { + displayName: 'Access Token URL', + name: 'accessTokenUrl', + type: 'string' as NodePropertyTypes, + default: 'https://nextcloud.example.com/apps/oauth2/api/v1/token', + required: true, + }, + { + displayName: 'Scope', + name: 'scope', + type: 'hidden' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Auth URI Query Parameters', + name: 'authQueryParameters', + type: 'hidden' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Authentication', + name: 'authentication', + type: 'hidden' as NodePropertyTypes, + default: 'body', + }, + ]; +} diff --git a/packages/nodes-base/nodes/NextCloud/GenericFunctions.ts b/packages/nodes-base/nodes/NextCloud/GenericFunctions.ts new file mode 100644 index 000000000..fc4e7646b --- /dev/null +++ b/packages/nodes-base/nodes/NextCloud/GenericFunctions.ts @@ -0,0 +1,64 @@ +import { + IExecuteFunctions, + IHookFunctions, +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; +import { OptionsWithUri } from 'request'; + +/** + * Make an API request to NextCloud + * + * @param {IHookFunctions} this + * @param {string} method + * @param {string} url + * @param {object} body + * @returns {Promise} + */ +export async function nextCloudApiRequest(this: IHookFunctions | IExecuteFunctions, method: string, endpoint: string, body: object | string | Buffer, headers?: object, encoding?: null | undefined, query?: object): Promise { // tslint:disable-line:no-any + const options : OptionsWithUri = { + headers, + method, + body, + qs: {}, + uri: '', + json: false, + }; + + if (encoding === null) { + options.encoding = null; + } + + const authenticationMethod = this.getNodeParameter('authentication', 0); + + try { + if (authenticationMethod === 'accessToken') { + const credentials = this.getCredentials('nextCloudApi'); + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + options.auth = { + user: credentials.user as string, + pass: credentials.password as string, + }; + + options.uri = `${credentials.webDavUrl}/${encodeURI(endpoint)}`; + + return await this.helpers.request(options); + } else { + const credentials = this.getCredentials('nextCloudOAuth2Api'); + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + options.uri = `${credentials.webDavUrl}/${encodeURI(endpoint)}`; + + return await this.helpers.requestOAuth2!.call(this, 'nextCloudOAuth2Api', options); + } + } catch (error) { + throw new Error(`NextCloud Error. Status Code: ${error.statusCode}. Message: ${error.message}`); + } +} diff --git a/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts b/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts index 309f53cea..8b3f0620f 100644 --- a/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts +++ b/packages/nodes-base/nodes/NextCloud/NextCloud.node.ts @@ -11,6 +11,7 @@ import { import { parseString } from 'xml2js'; import { OptionsWithUri } from 'request'; +import { nextCloudApiRequest } from './GenericFunctions'; export class NextCloud implements INodeType { @@ -32,9 +33,44 @@ export class NextCloud implements INodeType { { name: 'nextCloudApi', required: true, - } + displayOptions: { + show: { + authentication: [ + 'accessToken', + ], + }, + }, + }, + { + name: 'nextCloudOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: [ + 'oAuth2', + ], + }, + }, + }, ], properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'Access Token', + value: 'accessToken', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + ], + default: 'accessToken', + description: 'The resource to operate on.', + }, { displayName: 'Resource', name: 'resource', @@ -446,7 +482,14 @@ export class NextCloud implements INodeType { const items = this.getInputData().slice(); const returnData: IDataObject[] = []; - const credentials = this.getCredentials('nextCloudApi'); + const authenticationMethod = this.getNodeParameter('authentication', 0); + let credentials; + + if (authenticationMethod === 'accessToken') { + credentials = this.getCredentials('nextCloudApi'); + } else { + credentials = this.getCredentials('nextCloudOAuth2Api'); + } if (credentials === undefined) { throw new Error('No credentials got returned!'); @@ -562,26 +605,14 @@ export class NextCloud implements INodeType { webDavUrl = webDavUrl.slice(0, -1); } - const options: OptionsWithUri = { - auth: { - user: credentials.user as string, - pass: credentials.password as string, - }, - headers, - method: requestMethod, - body, - qs: {}, - uri: `${credentials.webDavUrl}/${encodeURI(endpoint)}`, - json: false, - }; - + let encoding = undefined; if (resource === 'file' && operation === 'download') { // Return the data as a buffer - options.encoding = null; + encoding = null; } try { - responseData = await this.helpers.request(options); + responseData = await nextCloudApiRequest.call(this, requestMethod, endpoint, body, headers, encoding); } catch (error) { if (this.continueOnFail() === true) { returnData.push({ error }); diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index dcfaeb226..c3a3183db 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -98,6 +98,7 @@ "dist/credentials/Msg91Api.credentials.js", "dist/credentials/MySql.credentials.js", "dist/credentials/NextCloudApi.credentials.js", + "dist/credentials/NextCloudOAuth2Api.credentials.js", "dist/credentials/OAuth1Api.credentials.js", "dist/credentials/OAuth2Api.credentials.js", "dist/credentials/OpenWeatherMapApi.credentials.js",