From 9172e3e6f579a91c8619f08a193c8d1fd06425fd Mon Sep 17 00:00:00 2001 From: Pavel Samusev Date: Mon, 23 Sep 2024 16:07:44 +0300 Subject: [PATCH 1/6] fix: prevent changing (update/delete) of "Hub-Based" connectors via CLI --- .../commands/delete/steps/remove-connector.ts | 18 +++---- .../publish/steps/update-connector.ts | 34 +++---------- .../src/commands/publish/types.ts | 1 + src/connector-cli/src/common/get-connector.ts | 49 +++++++++++++++++++ .../src/common/select-available-connector.ts | 4 +- 5 files changed, 69 insertions(+), 37 deletions(-) create mode 100644 src/connector-cli/src/common/get-connector.ts diff --git a/src/connector-cli/src/commands/delete/steps/remove-connector.ts b/src/connector-cli/src/commands/delete/steps/remove-connector.ts index e274a27..a2771ad 100644 --- a/src/connector-cli/src/commands/delete/steps/remove-connector.ts +++ b/src/connector-cli/src/commands/delete/steps/remove-connector.ts @@ -1,5 +1,5 @@ -import { selectAvailableConnector } from '../../../common/select-available-connector'; -import { httpErrorHandler, info, success, verbose, warn } from '../../../core'; +import { getConnectorById } from '../../../common/get-connector'; +import { httpErrorHandler, info, success, verbose } from '../../../core'; export async function removeConnector( connectorEndpointBaseUrl: string, @@ -9,6 +9,12 @@ export async function removeConnector( info('Removing connector...'); const deleteConnectorEnpdoint = `${connectorEndpointBaseUrl}/${connectorId}`; + await getConnectorById({ + baseUrl: connectorEndpointBaseUrl, + connectorId, + token, + }); + verbose('Removing connector via -> ' + deleteConnectorEnpdoint); const res = await fetch(deleteConnectorEnpdoint, { @@ -19,13 +25,7 @@ export async function removeConnector( }, }); - if (!res.ok && res.status === 404) { - warn( - `Connector with id ${connectorId} doesn't exist or you don't have permission to remove it` - ); - const id = await selectAvailableConnector(connectorEndpointBaseUrl, token); - return removeConnector(connectorEndpointBaseUrl, id, token); - } else if (!res.ok) { + if (!res.ok) { await httpErrorHandler(res); } diff --git a/src/connector-cli/src/commands/publish/steps/update-connector.ts b/src/connector-cli/src/commands/publish/steps/update-connector.ts index 45ff770..248d72a 100644 --- a/src/connector-cli/src/commands/publish/steps/update-connector.ts +++ b/src/connector-cli/src/commands/publish/steps/update-connector.ts @@ -1,5 +1,5 @@ -import { selectAvailableConnector } from '../../../common/select-available-connector'; -import { httpErrorHandler, info, success, verbose, warn } from '../../../core'; +import { getConnectorById } from '../../../common/get-connector'; +import { httpErrorHandler, info, success, verbose } from '../../../core'; import { UpdateConnectorPayload } from '../types'; export async function updateExistingConnector( @@ -9,37 +9,17 @@ export async function updateExistingConnector( payload: UpdateConnectorPayload ): Promise { info('Updating connector...'); - const getConnectorEnpdoint = `${connectorEndpointBaseUrl}/${connectorId}`; - - verbose( - `Checking connector's existing with id ${connectorId} -> ${getConnectorEnpdoint}` - ); - const existingConnectorRes = await fetch(getConnectorEnpdoint, { - headers: { - Authorization: token, - }, + const existingConnector = await getConnectorById({ + baseUrl: connectorEndpointBaseUrl, + connectorId, + token, }); - if (existingConnectorRes.status !== 200) { - warn( - `Connector with id ${connectorId} doesn't exist or you don't have permission to update it` - ); - // When connector is not available we request the list and ask user to select connector for update - const id = await selectAvailableConnector(connectorEndpointBaseUrl, token); - return updateExistingConnector( - connectorEndpointBaseUrl, - id, - token, - payload - ); - } - - const existingConnector = await existingConnectorRes.json(); const updatePayload = { ...existingConnector, ...payload, }; - const updateConnectorEnpdoint = getConnectorEnpdoint; + const updateConnectorEnpdoint = `${connectorEndpointBaseUrl}/${connectorId}`; verbose( `Deploying connector with a payload\n ${JSON.stringify( diff --git a/src/connector-cli/src/commands/publish/types.ts b/src/connector-cli/src/commands/publish/types.ts index aa7dbf7..4e15b97 100644 --- a/src/connector-cli/src/commands/publish/types.ts +++ b/src/connector-cli/src/commands/publish/types.ts @@ -11,6 +11,7 @@ interface ConnectorPayload { iconUrl?: string; script: string; apiVersion: string; + options?: Record; allowedDomains: Array; proxyOptions: { forwardedHeaders: boolean; diff --git a/src/connector-cli/src/common/get-connector.ts b/src/connector-cli/src/common/get-connector.ts new file mode 100644 index 0000000..d369a8f --- /dev/null +++ b/src/connector-cli/src/common/get-connector.ts @@ -0,0 +1,49 @@ +import { httpErrorHandler, verbose, warn } from '../core'; +import { ExecutionError } from '../core/types'; +import { selectAvailableConnector } from './select-available-connector'; + +interface GetConnectorById { + baseUrl: string; + connectorId: string; + token: string; +} + +interface EnvironmentConnector { + ownerType: 'grafx'; + externalSourceId?: string; +} + +export async function getConnectorById( + request: GetConnectorById +): Promise { + const getConnectorEnpdoint = `${request.baseUrl}/${request.connectorId}`; + + verbose( + `Checking connector's existing with id ${request.connectorId} -> ${getConnectorEnpdoint}` + ); + const res = await fetch(getConnectorEnpdoint, { + headers: { + Authorization: request.token, + }, + }); + + if (!res.ok && res.status === 404) { + warn( + `Connector with id ${request.connectorId} doesn't exist or you don't have permission to view it` + ); + // When particular connector is not available we request the full list and ask user to select one available to make an action + const id = await selectAvailableConnector(request.baseUrl, request.token); + return getConnectorById({ ...request, connectorId: id }); + } else if (!res.ok) { + await httpErrorHandler(res); + } + + const connector: EnvironmentConnector = await res.json(); + + if (connector.ownerType === 'grafx' && !!connector.externalSourceId) { + throw new ExecutionError( + `You're trying to change a connector created using Connector Hub. It can not be done via Connector CLI. Consider to use GraFx Platform connectors configuration page instead` + ); + } + return connector; +} diff --git a/src/connector-cli/src/common/select-available-connector.ts b/src/connector-cli/src/common/select-available-connector.ts index 4e3be48..1ffae13 100644 --- a/src/connector-cli/src/common/select-available-connector.ts +++ b/src/connector-cli/src/common/select-available-connector.ts @@ -30,7 +30,9 @@ export async function selectAvailableConnector( while (true) { // Use the question method to get the user input const index = Number( - await rl.question('Select the index of the connector to make an action') + await rl.question( + 'Select the index of the connector to make an action:\xa0' + ) ); // Use the validation function to check the input if (isNaN(index) || !connectors[index]) { From 71135e4a0f182a8175ae56c0e49fc223f7d53606 Mon Sep 17 00:00:00 2001 From: Pavel Samusev Date: Wed, 25 Sep 2024 10:33:23 +0300 Subject: [PATCH 2/6] chore: include "supportedAuth" to gen template --- src/connector-cli/src/commands/init/templates.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/connector-cli/src/commands/init/templates.ts b/src/connector-cli/src/commands/init/templates.ts index fad3bd1..7172793 100644 --- a/src/connector-cli/src/commands/init/templates.ts +++ b/src/connector-cli/src/commands/init/templates.ts @@ -18,6 +18,7 @@ export const getPackageJson = (projectName: string, type: Type) => ({ type: type, options: {}, mappings: {}, + supportedAuth: [], }, license: 'MIT', main: `${outputDirectory}/${outputFilename}`, From 1944bc9adc895e090c31b1a4edfa9dea7be1ff4c Mon Sep 17 00:00:00 2001 From: Pavel Samusev Date: Wed, 25 Sep 2024 10:44:10 +0300 Subject: [PATCH 3/6] chore: add description to forwardedHeaders params of "publish" command --- src/connector-cli/src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/connector-cli/src/index.ts b/src/connector-cli/src/index.ts index 7a209cd..54e5468 100644 --- a/src/connector-cli/src/index.ts +++ b/src/connector-cli/src/index.ts @@ -94,7 +94,10 @@ function main() { ' You can use "*" to denote dynamic parts of the URL.' + ' Example usage: --proxyOption.allowedDomains "main-domain.com", --proxyOption.allowedDomains "*.sub-domain.com"' ) - .option('--proxyOption.forwardedHeaders') + .option( + '--proxyOption.forwardedHeaders', + 'If enabled, any request to connector via proxy ednpoint will include X-Forwarded-* headers' + ) .action(withErrorHandlerAction(runPublish)); program From 6f2a7d6c5385e5de713015341d8b9b30413c4bb6 Mon Sep 17 00:00:00 2001 From: Pavel Samusev Date: Wed, 25 Sep 2024 11:56:24 +0300 Subject: [PATCH 4/6] chore: improve commands --- .../commands/delete/steps/remove-connector.ts | 11 ++++++----- .../commands/publish/steps/update-connector.ts | 5 +++-- .../src/commands/set-auth/index.ts | 18 +++++++++++++++--- src/connector-cli/src/common/get-connector.ts | 3 +++ 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/connector-cli/src/commands/delete/steps/remove-connector.ts b/src/connector-cli/src/commands/delete/steps/remove-connector.ts index a2771ad..c83bbeb 100644 --- a/src/connector-cli/src/commands/delete/steps/remove-connector.ts +++ b/src/connector-cli/src/commands/delete/steps/remove-connector.ts @@ -6,15 +6,16 @@ export async function removeConnector( connectorId: string, token: string ): Promise { - info('Removing connector...'); - const deleteConnectorEnpdoint = `${connectorEndpointBaseUrl}/${connectorId}`; - - await getConnectorById({ + info('Retrieving connector to remove...'); + const { id, name } = await getConnectorById({ baseUrl: connectorEndpointBaseUrl, connectorId, token, }); + info('Removing connector...'); + const deleteConnectorEnpdoint = `${connectorEndpointBaseUrl}/${id}`; + verbose('Removing connector via -> ' + deleteConnectorEnpdoint); const res = await fetch(deleteConnectorEnpdoint, { @@ -29,5 +30,5 @@ export async function removeConnector( await httpErrorHandler(res); } - success(`Connector "${connectorId}" is removed`); + success(`Connector "${name}" is removed`, { id, name }); } diff --git a/src/connector-cli/src/commands/publish/steps/update-connector.ts b/src/connector-cli/src/commands/publish/steps/update-connector.ts index 248d72a..d925502 100644 --- a/src/connector-cli/src/commands/publish/steps/update-connector.ts +++ b/src/connector-cli/src/commands/publish/steps/update-connector.ts @@ -8,7 +8,7 @@ export async function updateExistingConnector( token: string, payload: UpdateConnectorPayload ): Promise { - info('Updating connector...'); + info('Retrieving connector to remove...'); const existingConnector = await getConnectorById({ baseUrl: connectorEndpointBaseUrl, connectorId, @@ -19,7 +19,8 @@ export async function updateExistingConnector( ...payload, }; - const updateConnectorEnpdoint = `${connectorEndpointBaseUrl}/${connectorId}`; + info('Updating connector...'); + const updateConnectorEnpdoint = `${connectorEndpointBaseUrl}/${existingConnector.id}`; verbose( `Deploying connector with a payload\n ${JSON.stringify( diff --git a/src/connector-cli/src/commands/set-auth/index.ts b/src/connector-cli/src/commands/set-auth/index.ts index c98a886..751d8ec 100644 --- a/src/connector-cli/src/commands/set-auth/index.ts +++ b/src/connector-cli/src/commands/set-auth/index.ts @@ -1,3 +1,4 @@ +import { getConnectorById } from '../../common/get-connector'; import { info, readConnectorConfig, startCommand, success } from '../../core'; import { readAccessToken } from '../../core/read-access-token'; import { @@ -40,7 +41,7 @@ export async function runSetAuth( const connectorConfig = readConnectorConfig(packageJson); - if (!connectorConfig.supportedAuth) { + if (connectorConfig.supportedAuth.length === 0) { throw new ExecutionError( 'There is no information about supported authentication for this connector. Specify "config.supportedAuth" in connecotr\'s package.json' ); @@ -56,8 +57,15 @@ export async function runSetAuth( connectorConfig.supportedAuth ); + info('Retrieving connector to update...'); + const { id, name } = await getConnectorById({ + baseUrl, + connectorId, + token: accessToken, + }); + info('Build full request URL...'); - const requestUrl = getRequestUrl(baseUrl, environment, connectorId, type); + const requestUrl = getRequestUrl(baseUrl, environment, id, type); info('Set authentication...'); await setAuthentication( @@ -69,5 +77,9 @@ export async function runSetAuth( accessToken ); - success(`"${type}" authentication is applied.`, { id: connectorId }); + success(`"${type}" authentication is applied for "${name}" connector`, { + id, + name, + type, + }); } diff --git a/src/connector-cli/src/common/get-connector.ts b/src/connector-cli/src/common/get-connector.ts index d369a8f..a6451f5 100644 --- a/src/connector-cli/src/common/get-connector.ts +++ b/src/connector-cli/src/common/get-connector.ts @@ -9,6 +9,8 @@ interface GetConnectorById { } interface EnvironmentConnector { + id: string; + name: string; ownerType: 'grafx'; externalSourceId?: string; } @@ -40,6 +42,7 @@ export async function getConnectorById( const connector: EnvironmentConnector = await res.json(); + verbose('Checking belongings to "Hub-Based" connectors'); if (connector.ownerType === 'grafx' && !!connector.externalSourceId) { throw new ExecutionError( `You're trying to change a connector created using Connector Hub. It can not be done via Connector CLI. Consider to use GraFx Platform connectors configuration page instead` From 70ea16710120cb9d2a029796eb7d9aa3441e319d Mon Sep 17 00:00:00 2001 From: Pavel Samusev Date: Wed, 25 Sep 2024 12:58:30 +0300 Subject: [PATCH 5/6] chore: make "name" as optional --- .../authentication-types/chili-token.json | 1 - .../oauth2-authorization-code.json | 7 +------ .../oauth2-client-credentials.json | 2 +- .../oauth2-resource-owner-password.json | 1 - .../authentication-types/static-key.json | 2 +- .../src/commands/set-auth/index.ts | 6 +++++- src/connector-cli/src/core/types/gen-types.ts | 20 +++++++++---------- 7 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/connector-cli/resources/schemas/authentication-types/chili-token.json b/src/connector-cli/resources/schemas/authentication-types/chili-token.json index bcf5179..c6ca506 100644 --- a/src/connector-cli/resources/schemas/authentication-types/chili-token.json +++ b/src/connector-cli/resources/schemas/authentication-types/chili-token.json @@ -6,6 +6,5 @@ "type": "string" } }, - "required": ["name"], "additionalProperties": false } diff --git a/src/connector-cli/resources/schemas/authentication-types/oauth2-authorization-code.json b/src/connector-cli/resources/schemas/authentication-types/oauth2-authorization-code.json index 140a836..62cff88 100644 --- a/src/connector-cli/resources/schemas/authentication-types/oauth2-authorization-code.json +++ b/src/connector-cli/resources/schemas/authentication-types/oauth2-authorization-code.json @@ -60,11 +60,6 @@ "$ref": "#/$defs/SpecCustomization" } }, - "required": [ - "name", - "clientId", - "clientSecret", - "authorizationServerMetadata" - ], + "required": ["clientId", "clientSecret", "authorizationServerMetadata"], "additionalProperties": false } diff --git a/src/connector-cli/resources/schemas/authentication-types/oauth2-client-credentials.json b/src/connector-cli/resources/schemas/authentication-types/oauth2-client-credentials.json index 2ad12c8..04f25f2 100644 --- a/src/connector-cli/resources/schemas/authentication-types/oauth2-client-credentials.json +++ b/src/connector-cli/resources/schemas/authentication-types/oauth2-client-credentials.json @@ -18,6 +18,6 @@ "type": "string" } }, - "required": ["name", "clientId", "clientSecret", "tokenEndpoint"], + "required": ["clientId", "clientSecret", "tokenEndpoint"], "additionalProperties": false } diff --git a/src/connector-cli/resources/schemas/authentication-types/oauth2-resource-owner-password.json b/src/connector-cli/resources/schemas/authentication-types/oauth2-resource-owner-password.json index bfc5abd..2578167 100644 --- a/src/connector-cli/resources/schemas/authentication-types/oauth2-resource-owner-password.json +++ b/src/connector-cli/resources/schemas/authentication-types/oauth2-resource-owner-password.json @@ -30,7 +30,6 @@ } }, "required": [ - "name", "clientId", "clientSecret", "tokenEndpoint", diff --git a/src/connector-cli/resources/schemas/authentication-types/static-key.json b/src/connector-cli/resources/schemas/authentication-types/static-key.json index 59ae064..31663c4 100644 --- a/src/connector-cli/resources/schemas/authentication-types/static-key.json +++ b/src/connector-cli/resources/schemas/authentication-types/static-key.json @@ -12,6 +12,6 @@ "type": "string" } }, - "required": ["name", "key", "value"], + "required": ["key", "value"], "additionalProperties": false } diff --git a/src/connector-cli/src/commands/set-auth/index.ts b/src/connector-cli/src/commands/set-auth/index.ts index 751d8ec..c3cc510 100644 --- a/src/connector-cli/src/commands/set-auth/index.ts +++ b/src/connector-cli/src/commands/set-auth/index.ts @@ -43,7 +43,7 @@ export async function runSetAuth( if (connectorConfig.supportedAuth.length === 0) { throw new ExecutionError( - 'There is no information about supported authentication for this connector. Specify "config.supportedAuth" in connecotr\'s package.json' + 'There is no information about supported authentication for this connector. Specify "config.supportedAuth" in connector\'s package.json' ); } @@ -64,6 +64,10 @@ export async function runSetAuth( token: accessToken, }); + if (!authData.name) { + authData.name = `${id}-${usage}-${type}`; + } + info('Build full request URL...'); const requestUrl = getRequestUrl(baseUrl, environment, id, type); diff --git a/src/connector-cli/src/core/types/gen-types.ts b/src/connector-cli/src/core/types/gen-types.ts index 0980f62..6576160 100644 --- a/src/connector-cli/src/core/types/gen-types.ts +++ b/src/connector-cli/src/core/types/gen-types.ts @@ -13,14 +13,14 @@ // match the expected interface, even if the JSON is valid. export interface ChiliToken { - name: string; + name?: string; } export interface Oauth2AuthorizationCode { authorizationServerMetadata: AuthorizationServerMetadata; clientId: string; clientSecret: string; - name: string; + name?: string; scope?: string; specCustomization?: SpecCustomization; } @@ -49,7 +49,7 @@ export enum RequestContentType { export interface Oauth2ClientCredentials { clientId: string; clientSecret: string; - name: string; + name?: string; scope?: string; tokenEndpoint: string; } @@ -58,7 +58,7 @@ export interface Oauth2ResourceOwnerPassword { bodyFormat?: RequestContentType; clientId: string; clientSecret: string; - name: string; + name?: string; password: string; scope?: string; tokenEndpoint: string; @@ -67,7 +67,7 @@ export interface Oauth2ResourceOwnerPassword { export interface StaticKey { key: string; - name: string; + name?: string; value: string; } @@ -298,13 +298,13 @@ function r(name: string) { const typeMap: any = { "ChiliToken": o([ - { json: "name", js: "name", typ: "" }, + { json: "name", js: "name", typ: u(undefined, "") }, ], false), "Oauth2AuthorizationCode": o([ { json: "authorizationServerMetadata", js: "authorizationServerMetadata", typ: r("AuthorizationServerMetadata") }, { json: "clientId", js: "clientId", typ: "" }, { json: "clientSecret", js: "clientSecret", typ: "" }, - { json: "name", js: "name", typ: "" }, + { json: "name", js: "name", typ: u(undefined, "") }, { json: "scope", js: "scope", typ: u(undefined, "") }, { json: "specCustomization", js: "specCustomization", typ: u(undefined, r("SpecCustomization")) }, ], false), @@ -320,7 +320,7 @@ const typeMap: any = { "Oauth2ClientCredentials": o([ { json: "clientId", js: "clientId", typ: "" }, { json: "clientSecret", js: "clientSecret", typ: "" }, - { json: "name", js: "name", typ: "" }, + { json: "name", js: "name", typ: u(undefined, "") }, { json: "scope", js: "scope", typ: u(undefined, "") }, { json: "tokenEndpoint", js: "tokenEndpoint", typ: "" }, ], false), @@ -328,7 +328,7 @@ const typeMap: any = { { json: "bodyFormat", js: "bodyFormat", typ: u(undefined, r("RequestContentType")) }, { json: "clientId", js: "clientId", typ: "" }, { json: "clientSecret", js: "clientSecret", typ: "" }, - { json: "name", js: "name", typ: "" }, + { json: "name", js: "name", typ: u(undefined, "") }, { json: "password", js: "password", typ: "" }, { json: "scope", js: "scope", typ: u(undefined, "") }, { json: "tokenEndpoint", js: "tokenEndpoint", typ: "" }, @@ -336,7 +336,7 @@ const typeMap: any = { ], false), "StaticKey": o([ { json: "key", js: "key", typ: "" }, - { json: "name", js: "name", typ: "" }, + { json: "name", js: "name", typ: u(undefined, "") }, { json: "value", js: "value", typ: "" }, ], false), "ConnectorConfig": o([ From 4db711089f5c8090123fe14295d4a83a39bfadba Mon Sep 17 00:00:00 2001 From: Pavel Samusev Date: Wed, 25 Sep 2024 15:32:49 +0300 Subject: [PATCH 6/6] chore: fix request url --- src/connector-cli/src/commands/set-auth/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/connector-cli/src/commands/set-auth/index.ts b/src/connector-cli/src/commands/set-auth/index.ts index c3cc510..c0ec5a3 100644 --- a/src/connector-cli/src/commands/set-auth/index.ts +++ b/src/connector-cli/src/commands/set-auth/index.ts @@ -1,3 +1,4 @@ +import { buildRequestUrl } from '../../common/build-request-url'; import { getConnectorById } from '../../common/get-connector'; import { info, readConnectorConfig, startCommand, success } from '../../core'; import { readAccessToken } from '../../core/read-access-token'; @@ -59,7 +60,7 @@ export async function runSetAuth( info('Retrieving connector to update...'); const { id, name } = await getConnectorById({ - baseUrl, + baseUrl: buildRequestUrl(baseUrl, environment), connectorId, token: accessToken, });