@unito/integration-cli 0.57.2 → 0.58.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.eslintrc.d.ts +6 -0
- package/dist/.eslintrc.js +17 -2
- package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/src/index.ts +1 -3
- package/dist/src/commands/dev.d.ts +4 -0
- package/dist/src/commands/dev.js +40 -4
- package/dist/src/commands/init.js +2 -2
- package/dist/src/commands/login.js +1 -1
- package/dist/src/commands/oauth2.js +27 -21
- package/dist/src/commands/test.js +28 -6
- package/dist/src/errors.d.ts +8 -0
- package/dist/src/errors.js +35 -18
- package/dist/src/resources/configuration.d.ts +1 -1
- package/dist/src/resources/configuration.js +4 -6
- package/dist/src/resources/credentials.d.ts +3 -0
- package/dist/src/resources/credentials.js +26 -0
- package/dist/src/resources/integrations.d.ts +1 -0
- package/dist/src/resources/integrations.js +37 -2
- package/dist/src/resources/oauth2.d.ts +4 -0
- package/dist/src/resources/oauth2.js +28 -0
- package/dist/src/resources/template.d.ts +3 -0
- package/dist/src/resources/template.js +40 -0
- package/dist/src/services/integrationsPlatform.d.ts +1 -0
- package/dist/src/services/integrationsPlatform.js +5 -1
- package/dist/src/{oauth2Helper/oauth2Helper.d.ts → services/oauth2.d.ts} +37 -23
- package/dist/src/{oauth2Helper/oauth2Helper.js → services/oauth2.js} +112 -113
- package/dist/test/commands/dev.test.js +21 -0
- package/dist/test/commands/init.test.js +4 -4
- package/dist/test/commands/login.test.js +1 -0
- package/dist/test/commands/oauth2.test.js +20 -2
- package/dist/test/commands/publish.test.js +1 -0
- package/dist/test/commands/test.test.js +22 -0
- package/dist/test/commands/upgrade.test.js +1 -0
- package/dist/test/resources/integrations.test.js +37 -0
- package/dist/test/resources/oauth2.test.js +55 -0
- package/dist/test/resources/template.test.js +40 -0
- package/dist/test/services/oauth2.test.d.ts +1 -0
- package/dist/test/services/oauth2.test.js +159 -0
- package/oclif.manifest.json +64 -9
- package/package.json +8 -8
- package/dist/integrationGenerator/errors.d.ts +0 -2
- package/dist/integrationGenerator/errors.js +0 -6
- package/dist/integrationGenerator/index.d.ts +0 -2
- package/dist/integrationGenerator/index.js +0 -5
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.dockerignore +0 -3
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.eslintrc.js +0 -74
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.nvmrc +0 -1
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.prettierignore +0 -1
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.prettierrc +0 -7
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.unito.json +0 -1
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/Dockerfile +0 -38
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/README.md +0 -21
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/src/index.ts +0 -94
- package/dist/integrationGenerator/integrationBoilerplate/package.json +0 -43
- package/dist/integrationGenerator/integrationBoilerplate/src/logger.ts +0 -55
- package/dist/integrationGenerator/integrationBoilerplate/src/middlewares/additionalLoggingContext.ts +0 -22
- package/dist/integrationGenerator/integrationBoilerplate/src/middlewares/correlationId.ts +0 -13
- package/dist/integrationGenerator/integrationBoilerplate/src/middlewares/credentials.ts +0 -38
- package/dist/integrationGenerator/integrationBoilerplate/src/request.ts +0 -59
- package/dist/integrationGenerator/integrationBoilerplate/src/routes/index.ts +0 -11
- package/dist/integrationGenerator/integrationBoilerplate/src/routes/me.ts +0 -15
- package/dist/integrationGenerator/integrationBoilerplate/src/routes/root.ts +0 -12
- package/dist/integrationGenerator/integrationBoilerplate/tsconfig.json +0 -37
- package/dist/integrationGenerator/src/index.d.ts +0 -1
- package/dist/integrationGenerator/src/index.js +0 -5
- package/dist/integrationGenerator/src/resources/index.d.ts +0 -1
- package/dist/integrationGenerator/src/resources/index.js +0 -5
- package/dist/integrationGenerator/src/resources/integration.d.ts +0 -9
- package/dist/integrationGenerator/src/resources/integration.js +0 -60
- package/dist/integrationGenerator/test/resources/integration.test.js +0 -50
- package/dist/src/oauth2Helper/types.d.ts +0 -22
- package/dist/src/oauth2Helper/types.js +0 -2
- package/dist/src/services/oauth2Helper.d.ts +0 -4
- package/dist/src/services/oauth2Helper.js +0 -35
- package/dist/test/oauth2Helper/oauth2Helper.test.js +0 -150
- package/dist/test/services/oauth2Helper.test.js +0 -85
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/.dockerignore +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/.eslintrc.js +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/.nvmrc +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/.prettierignore +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/.prettierrc +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/.unito.json +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/Dockerfile +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/README.md +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/package.json +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/logger.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/middlewares/additionalLoggingContext.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/middlewares/correlationId.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/middlewares/credentials.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/request.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/routes/index.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/routes/me.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/routes/root.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/tsconfig.json +0 -0
- /package/dist/{integrationGenerator/test/resources/integration.test.d.ts → test/resources/integrations.test.d.ts} +0 -0
- /package/dist/test/{oauth2Helper/oauth2Helper.test.d.ts → resources/oauth2.test.d.ts} +0 -0
- /package/dist/test/{services/oauth2Helper.test.d.ts → resources/template.test.d.ts} +0 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Oauth2Response, Oauth2Payload } from '../services/oauth2';
|
|
2
|
+
import { Environment } from './globalConfiguration';
|
|
3
|
+
export declare function performOAuth2Flow(applicationCredentials: Oauth2Payload, environment?: Environment, credentialPayload?: Record<string, unknown>): Promise<Oauth2Response>;
|
|
4
|
+
export declare function updateToken(applicationCredentials: Oauth2Payload, refreshToken: string): Promise<Oauth2Response>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.updateToken = exports.performOAuth2Flow = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const oauth2_1 = tslib_1.__importDefault(require("../services/oauth2"));
|
|
6
|
+
const errors_1 = require("../errors");
|
|
7
|
+
const globalConfiguration_1 = require("./globalConfiguration");
|
|
8
|
+
async function performOAuth2Flow(applicationCredentials, environment = globalConfiguration_1.Environment.Production, credentialPayload) {
|
|
9
|
+
const oauthHelper = new oauth2_1.default(applicationCredentials, environment, credentialPayload);
|
|
10
|
+
const serverUrl = await oauthHelper.startServer();
|
|
11
|
+
const healthCheck = await fetch(`${serverUrl}/health`);
|
|
12
|
+
if (healthCheck.status !== 200) {
|
|
13
|
+
throw new Error('OAuthServer did not start');
|
|
14
|
+
}
|
|
15
|
+
await oauthHelper.authorize();
|
|
16
|
+
const oauth2Response = await oauthHelper.callbackIsDone();
|
|
17
|
+
await oauthHelper.stopServer();
|
|
18
|
+
if (!oauth2Response.accessToken) {
|
|
19
|
+
throw new errors_1.FailedToRetrieveAccessTokenError('Access token was not returned');
|
|
20
|
+
}
|
|
21
|
+
return oauth2Response;
|
|
22
|
+
}
|
|
23
|
+
exports.performOAuth2Flow = performOAuth2Flow;
|
|
24
|
+
async function updateToken(applicationCredentials, refreshToken) {
|
|
25
|
+
const oauthHelper = new oauth2_1.default(applicationCredentials);
|
|
26
|
+
return oauthHelper.updateToken(refreshToken);
|
|
27
|
+
}
|
|
28
|
+
exports.updateToken = updateToken;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseTemplate = void 0;
|
|
4
|
+
const operators = Object.freeze(['+']);
|
|
5
|
+
function encode(value) {
|
|
6
|
+
return value
|
|
7
|
+
? value
|
|
8
|
+
.toString()
|
|
9
|
+
.split(/(%[0-9A-Fa-f]{2})/g)
|
|
10
|
+
.map(function (part) {
|
|
11
|
+
if (!/%[0-9A-Fa-f]/.test(part)) {
|
|
12
|
+
part = encodeURI(part).replace(/%5B/g, '[').replace(/%5D/g, ']');
|
|
13
|
+
}
|
|
14
|
+
return part;
|
|
15
|
+
})
|
|
16
|
+
.join('')
|
|
17
|
+
: '';
|
|
18
|
+
}
|
|
19
|
+
function parseTemplate(template) {
|
|
20
|
+
return {
|
|
21
|
+
expand: function (context) {
|
|
22
|
+
return template.replace(/\{([^{}]+)\}|([^{}]+)/g, function (_, expression, literal) {
|
|
23
|
+
if (expression) {
|
|
24
|
+
if (operators.indexOf(expression.charAt(0)) !== -1) {
|
|
25
|
+
expression = expression.substr(1);
|
|
26
|
+
return encode(context[expression]);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
// For now, if no '+' operator is present, do not expand
|
|
30
|
+
return `{${expression}}`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
return encode(literal);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
exports.parseTemplate = parseTemplate;
|
|
@@ -38,3 +38,4 @@ export declare function getIntegrationEvents(integrationId: number, options?: {
|
|
|
38
38
|
limit?: number;
|
|
39
39
|
}): Promise<IntegrationEvent[]>;
|
|
40
40
|
export declare function updateIntegration(integrationId: number, configuration: Configuration): Promise<Integration>;
|
|
41
|
+
export declare function getCredential(credentialId: number): Promise<Credential>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.updateIntegration = exports.getIntegrationEvents = exports.inviteUserToIntegration = exports.createIntegration = exports.publishIntegration = exports.getIntegrations = exports.getIntegrationByName = exports.getIntegration = exports.reencryptData = exports.decryptData = exports.encryptData = exports.getProfile = exports.setApiKey = exports.getApiKey = exports.setEnvironment = exports.Servers = exports.HttpError = void 0;
|
|
3
|
+
exports.getCredential = exports.updateIntegration = exports.getIntegrationEvents = exports.inviteUserToIntegration = exports.createIntegration = exports.publishIntegration = exports.getIntegrations = exports.getIntegrationByName = exports.getIntegration = exports.reencryptData = exports.decryptData = exports.encryptData = exports.getProfile = exports.setApiKey = exports.getApiKey = exports.setEnvironment = exports.Servers = exports.HttpError = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const fs = tslib_1.__importStar(require("fs"));
|
|
6
6
|
const integrations_platform_client_1 = tslib_1.__importDefault(require("@unito/integrations-platform-client"));
|
|
@@ -153,3 +153,7 @@ async function updateIntegration(integrationId, configuration) {
|
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
155
|
exports.updateIntegration = updateIntegration;
|
|
156
|
+
async function getCredential(credentialId) {
|
|
157
|
+
return integrations_platform_client_1.default.getCredentialById(credentialId);
|
|
158
|
+
}
|
|
159
|
+
exports.getCredential = getCredential;
|
|
@@ -1,8 +1,31 @@
|
|
|
1
1
|
import * as openUrl from 'openurl';
|
|
2
|
-
import {
|
|
2
|
+
import { Oauth2 } from '../configurationTypes';
|
|
3
3
|
import { Environment } from '../resources/globalConfiguration';
|
|
4
4
|
export declare const open: typeof openUrl;
|
|
5
|
-
declare
|
|
5
|
+
export declare const HTML_ERROR_MSG = "<!doctype html><head><title>Unito</title></head><body style=\"text-align:center\"> An unknown error occured </body>";
|
|
6
|
+
export declare const HTML_SUCCESS_MSG = "<!doctype html><head><title>Unito</title></head><body style=\"text-align:center\"> Redirected to the CLI successfully </body>";
|
|
7
|
+
export interface Oauth2Credentials {
|
|
8
|
+
clientId: string;
|
|
9
|
+
clientSecret: string;
|
|
10
|
+
}
|
|
11
|
+
export interface AuthorizePayload extends Oauth2Credentials {
|
|
12
|
+
providerAuthorizationUrl: string;
|
|
13
|
+
scopes?: string;
|
|
14
|
+
providerAccessTokenUrl: string;
|
|
15
|
+
}
|
|
16
|
+
export interface CallbackPayload {
|
|
17
|
+
code: string;
|
|
18
|
+
}
|
|
19
|
+
export interface Oauth2Response {
|
|
20
|
+
accessToken: string;
|
|
21
|
+
refreshToken?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface TokenPayload extends Oauth2Credentials {
|
|
24
|
+
providerTokenUrl: string;
|
|
25
|
+
code: string;
|
|
26
|
+
}
|
|
27
|
+
export type Oauth2Payload = Oauth2;
|
|
28
|
+
declare class OAuth2Service {
|
|
6
29
|
private environment;
|
|
7
30
|
private server;
|
|
8
31
|
private clientId;
|
|
@@ -12,12 +35,11 @@ declare class OAuth2Helper {
|
|
|
12
35
|
private scopes;
|
|
13
36
|
private grantType;
|
|
14
37
|
private requestContentType;
|
|
15
|
-
private
|
|
16
|
-
private refreshToken?;
|
|
17
|
-
private code;
|
|
38
|
+
private oauth2Response;
|
|
18
39
|
private serverUrl;
|
|
19
40
|
private tokenRequestParameters;
|
|
20
41
|
private refreshRequestParameters;
|
|
42
|
+
private credentialPayload;
|
|
21
43
|
/**
|
|
22
44
|
* Constructs an instance of OAuthHelper.
|
|
23
45
|
* @param clientId The client ID for your OAuth application.
|
|
@@ -26,27 +48,24 @@ declare class OAuth2Helper {
|
|
|
26
48
|
* @param scopes The scopes required for the OAuth authorization.
|
|
27
49
|
* @param providerTokenUrl The URL for the token endpoint of the provider.
|
|
28
50
|
*/
|
|
29
|
-
constructor(authorizationInfo: Oauth2Payload, environment?: Environment);
|
|
51
|
+
constructor(authorizationInfo: Oauth2Payload, environment?: Environment, credentialPayload?: Record<string, unknown>);
|
|
30
52
|
/**
|
|
31
|
-
*
|
|
32
|
-
* @param req The express Request object.
|
|
33
|
-
* @param res The express Response object.
|
|
53
|
+
* Initiate the authorization flow and redirects the user to the provider's authorization page.
|
|
34
54
|
*/
|
|
35
|
-
|
|
55
|
+
authorize(): Promise<void>;
|
|
36
56
|
/**
|
|
37
57
|
* Handles the callback request from the provider and stores the authorization code.
|
|
38
58
|
* @param req The express Request object.
|
|
39
59
|
* @param res The express Response object.
|
|
40
60
|
*/
|
|
41
61
|
private handleCallback;
|
|
42
|
-
private encodeBody;
|
|
43
62
|
/**
|
|
44
|
-
*
|
|
45
|
-
* @
|
|
46
|
-
* @param res The express Response object.
|
|
63
|
+
* Waits for the authorization code to be set.
|
|
64
|
+
* @returns A promise that resolves when the code is set.
|
|
47
65
|
*/
|
|
48
|
-
|
|
49
|
-
|
|
66
|
+
callbackIsDone(): Promise<Oauth2Response>;
|
|
67
|
+
private encodeBody;
|
|
68
|
+
updateToken(refreshToken: string): Promise<Oauth2Response>;
|
|
50
69
|
/**
|
|
51
70
|
* Starts the Express server for handling OAuth callbacks.
|
|
52
71
|
* @returns The URL of the server.
|
|
@@ -55,11 +74,6 @@ declare class OAuth2Helper {
|
|
|
55
74
|
/**
|
|
56
75
|
* Stops the Express server.
|
|
57
76
|
*/
|
|
58
|
-
stopServer(): void
|
|
59
|
-
/**
|
|
60
|
-
* Waits for the authorization code to be set.
|
|
61
|
-
* @returns A promise that resolves when the code is set.
|
|
62
|
-
*/
|
|
63
|
-
callbackIsDone(): Promise<boolean>;
|
|
77
|
+
stopServer(): Promise<void>;
|
|
64
78
|
}
|
|
65
|
-
export default
|
|
79
|
+
export default OAuth2Service;
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.open = void 0;
|
|
3
|
+
exports.HTML_SUCCESS_MSG = exports.HTML_ERROR_MSG = exports.open = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const express_1 = tslib_1.__importDefault(require("express"));
|
|
6
6
|
const cors_1 = tslib_1.__importDefault(require("cors"));
|
|
7
7
|
const openUrl = tslib_1.__importStar(require("openurl"));
|
|
8
8
|
const ngrok_1 = tslib_1.__importDefault(require("ngrok"));
|
|
9
|
-
const IntegrationsPlatformClient = tslib_1.__importStar(require("
|
|
9
|
+
const IntegrationsPlatformClient = tslib_1.__importStar(require("./integrationsPlatform"));
|
|
10
10
|
const configurationTypes_1 = require("../configurationTypes");
|
|
11
|
+
const template_1 = require("../resources/template");
|
|
11
12
|
const errors_1 = require("../errors");
|
|
12
13
|
const globalConfiguration_1 = require("../resources/globalConfiguration");
|
|
13
14
|
// It allows to stub openUrl library in the test
|
|
14
15
|
exports.open = openUrl;
|
|
15
|
-
|
|
16
|
+
exports.HTML_ERROR_MSG = `<!doctype html><head><title>Unito</title></head><body style="text-align:center"> An unknown error occured </body>`;
|
|
17
|
+
exports.HTML_SUCCESS_MSG = `<!doctype html><head><title>Unito</title></head><body style="text-align:center"> Redirected to the CLI successfully </body>`;
|
|
18
|
+
const AUTHORIZATION_RESPONSE_QUERY_PARAMS = ['code', 'state', 'error', 'error_description', 'error_uri'];
|
|
19
|
+
class OAuth2Service {
|
|
16
20
|
environment;
|
|
17
21
|
server = null;
|
|
18
22
|
clientId;
|
|
@@ -22,12 +26,11 @@ class OAuth2Helper {
|
|
|
22
26
|
scopes;
|
|
23
27
|
grantType;
|
|
24
28
|
requestContentType;
|
|
25
|
-
|
|
26
|
-
refreshToken;
|
|
27
|
-
code = '';
|
|
29
|
+
oauth2Response;
|
|
28
30
|
serverUrl = '';
|
|
29
31
|
tokenRequestParameters;
|
|
30
32
|
refreshRequestParameters;
|
|
33
|
+
credentialPayload;
|
|
31
34
|
/**
|
|
32
35
|
* Constructs an instance of OAuthHelper.
|
|
33
36
|
* @param clientId The client ID for your OAuth application.
|
|
@@ -36,13 +39,11 @@ class OAuth2Helper {
|
|
|
36
39
|
* @param scopes The scopes required for the OAuth authorization.
|
|
37
40
|
* @param providerTokenUrl The URL for the token endpoint of the provider.
|
|
38
41
|
*/
|
|
39
|
-
constructor(authorizationInfo, environment = globalConfiguration_1.Environment.Production) {
|
|
40
|
-
const { clientId, clientSecret, authorizationUrl, scopes, tokenUrl, grantType, requestContentType,
|
|
42
|
+
constructor(authorizationInfo, environment = globalConfiguration_1.Environment.Production, credentialPayload) {
|
|
43
|
+
const { clientId, clientSecret, authorizationUrl, scopes, tokenUrl, grantType, requestContentType, refreshRequestParameters, tokenRequestParameters, } = authorizationInfo;
|
|
41
44
|
this.startServer = this.startServer.bind(this);
|
|
42
45
|
this.stopServer = this.stopServer.bind(this);
|
|
43
|
-
this.handleAuthorize = this.handleAuthorize.bind(this);
|
|
44
46
|
this.handleCallback = this.handleCallback.bind(this);
|
|
45
|
-
this.handleToken = this.handleToken.bind(this);
|
|
46
47
|
this.clientId = clientId;
|
|
47
48
|
this.clientSecret = clientSecret;
|
|
48
49
|
this.providerAuthorizationUrl = authorizationUrl;
|
|
@@ -50,39 +51,42 @@ class OAuth2Helper {
|
|
|
50
51
|
this.tokenUrl = tokenUrl;
|
|
51
52
|
this.grantType = grantType ?? configurationTypes_1.GrantType.AUTHORIZATION_CODE;
|
|
52
53
|
this.requestContentType = requestContentType ?? configurationTypes_1.RequestContentType.URL_ENCODED;
|
|
53
|
-
this.responseContentType = responseContentType ?? configurationTypes_1.RequestContentType.JSON;
|
|
54
|
-
this.refreshToken = refreshToken;
|
|
55
54
|
this.tokenRequestParameters = tokenRequestParameters;
|
|
56
55
|
this.refreshRequestParameters = refreshRequestParameters;
|
|
57
56
|
this.environment = environment;
|
|
57
|
+
this.credentialPayload = credentialPayload;
|
|
58
|
+
if (!Object.values(configurationTypes_1.RequestContentType).includes(this.requestContentType)) {
|
|
59
|
+
throw new errors_1.UnsupportedContentTypeError(`Request content type not supported: ${this.requestContentType}`);
|
|
60
|
+
}
|
|
58
61
|
}
|
|
59
62
|
/**
|
|
60
|
-
*
|
|
61
|
-
* @param req The express Request object.
|
|
62
|
-
* @param res The express Response object.
|
|
63
|
+
* Initiate the authorization flow and redirects the user to the provider's authorization page.
|
|
63
64
|
*/
|
|
64
|
-
async
|
|
65
|
+
async authorize() {
|
|
65
66
|
if (!this.providerAuthorizationUrl) {
|
|
66
|
-
|
|
67
|
-
return;
|
|
67
|
+
throw new errors_1.InvalidRequestContentTypeError('authorizationUrl must be defined in .unito.json');
|
|
68
68
|
}
|
|
69
|
-
const
|
|
70
|
-
const params = new URLSearchParams(authUrl.search);
|
|
69
|
+
const authorizationParams = new URLSearchParams();
|
|
71
70
|
if (this.clientId) {
|
|
72
|
-
|
|
71
|
+
authorizationParams.set('client_id', this.clientId);
|
|
73
72
|
}
|
|
74
|
-
params.set('redirect_uri', `${IntegrationsPlatformClient.Servers[this.environment]}/credentials/new/oauth2/callback-cli`);
|
|
75
|
-
const state = Buffer.from(JSON.stringify({
|
|
76
|
-
cliCallbackUrl: `${this.serverUrl}/credentials/new/oauth2/callback`,
|
|
77
|
-
})).toString('base64');
|
|
78
|
-
params.set('state', state);
|
|
79
73
|
if (this.scopes) {
|
|
80
|
-
|
|
74
|
+
authorizationParams.set('scope', this.scopes.join(' '));
|
|
81
75
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
76
|
+
const state = Buffer.from(JSON.stringify({
|
|
77
|
+
cliCallbackUrl: `${this.serverUrl}/oauth2/callback`,
|
|
78
|
+
})).toString('base64');
|
|
79
|
+
authorizationParams.set('state', state);
|
|
80
|
+
authorizationParams.set('response_type', 'code');
|
|
81
|
+
authorizationParams.set('redirect_uri', `${IntegrationsPlatformClient.Servers[this.environment]}/credentials/new/oauth2/callback-cli`);
|
|
82
|
+
const delimiter = this.providerAuthorizationUrl.includes('?') ? '&' : '?';
|
|
83
|
+
const authorizationUrlTemplate = `${this.providerAuthorizationUrl}${delimiter}${authorizationParams.toString()}`;
|
|
84
|
+
const authorizationUrl = (0, template_1.parseTemplate)(authorizationUrlTemplate).expand({
|
|
85
|
+
...(this.credentialPayload ?? {}),
|
|
86
|
+
...Object.fromEntries(authorizationParams.entries()),
|
|
87
|
+
});
|
|
88
|
+
console.log(' Calling the following authorization URL: \n ', authorizationUrl);
|
|
89
|
+
exports.open.open(authorizationUrl);
|
|
86
90
|
}
|
|
87
91
|
/**
|
|
88
92
|
* Handles the callback request from the provider and stores the authorization code.
|
|
@@ -90,83 +94,99 @@ class OAuth2Helper {
|
|
|
90
94
|
* @param res The express Response object.
|
|
91
95
|
*/
|
|
92
96
|
async handleCallback(req, res) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
this.code = 'error';
|
|
101
|
-
}
|
|
102
|
-
res.sendStatus(200);
|
|
103
|
-
}
|
|
104
|
-
encodeBody(bodyData, contentType) {
|
|
105
|
-
switch (contentType) {
|
|
106
|
-
case configurationTypes_1.RequestContentType.URL_ENCODED:
|
|
107
|
-
return new URLSearchParams(bodyData).toString();
|
|
108
|
-
case configurationTypes_1.RequestContentType.JSON:
|
|
109
|
-
return JSON.stringify(bodyData);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Handles the token request to exchange the authorization code for an access token.
|
|
114
|
-
* @param req The express Request object.
|
|
115
|
-
* @param res The express Response object.
|
|
116
|
-
*/
|
|
117
|
-
async handleToken(_req, res) {
|
|
118
|
-
if (!Object.values(configurationTypes_1.RequestContentType).includes(this.requestContentType)) {
|
|
119
|
-
res.status(400).json({ error: `Request content type not supported: ${this.requestContentType}` });
|
|
97
|
+
// Authorization Response: https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2
|
|
98
|
+
// const state = req.state as string;
|
|
99
|
+
// Error response: https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2.1
|
|
100
|
+
const error = req.query.error;
|
|
101
|
+
if (error) {
|
|
102
|
+
res.setHeader('Content-Type', 'text/html').send(exports.HTML_ERROR_MSG);
|
|
103
|
+
return;
|
|
120
104
|
}
|
|
121
|
-
|
|
122
|
-
|
|
105
|
+
// We keep all the non-standard query parameters of the authorization response
|
|
106
|
+
// so they can be used later on for the access token request (see tokenRequestParameters).
|
|
107
|
+
const authorizationResponseVariables = {};
|
|
108
|
+
for (const [key, value] of Object.entries(req.query)) {
|
|
109
|
+
if (!AUTHORIZATION_RESPONSE_QUERY_PARAMS.includes(key)) {
|
|
110
|
+
authorizationResponseVariables[`authorizationResponse.${key}`] = value?.toString() ?? '';
|
|
111
|
+
}
|
|
123
112
|
}
|
|
124
|
-
const
|
|
125
|
-
|
|
113
|
+
const templateVariables = { ...(this.credentialPayload ?? {}), ...authorizationResponseVariables };
|
|
114
|
+
const tokenRequestPayload = {
|
|
115
|
+
...Object.fromEntries(Object.entries(this.tokenRequestParameters?.body ?? {}).map(([key, value]) => [
|
|
116
|
+
key,
|
|
117
|
+
typeof value === 'string' ? decodeURIComponent((0, template_1.parseTemplate)(value).expand(templateVariables)) : value,
|
|
118
|
+
])),
|
|
126
119
|
grant_type: this.grantType,
|
|
120
|
+
code: req.query.code,
|
|
127
121
|
redirect_uri: `${IntegrationsPlatformClient.Servers[this.environment]}/credentials/new/oauth2/callback-cli`,
|
|
122
|
+
...(this.clientId && { client_id: this.clientId }),
|
|
123
|
+
...(this.clientSecret && { client_secret: this.clientSecret }),
|
|
128
124
|
};
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const body = this.encodeBody(bodyData, this.requestContentType);
|
|
136
|
-
const fetchOptions = {
|
|
137
|
-
headers: {
|
|
138
|
-
'Content-type': this.requestContentType,
|
|
139
|
-
...(this.tokenRequestParameters?.header ?? {}),
|
|
140
|
-
},
|
|
141
|
-
body,
|
|
142
|
-
method: 'POST',
|
|
125
|
+
const tokenRequestHeaders = {
|
|
126
|
+
'Content-Type': this.requestContentType,
|
|
127
|
+
...Object.fromEntries(Object.entries(this.tokenRequestParameters?.header ?? {}).map(([key, value]) => [
|
|
128
|
+
key,
|
|
129
|
+
decodeURIComponent((0, template_1.parseTemplate)(String(value)).expand(templateVariables)),
|
|
130
|
+
])),
|
|
143
131
|
};
|
|
144
132
|
try {
|
|
145
|
-
const fetchResult = await fetch(this.tokenUrl,
|
|
133
|
+
const fetchResult = await fetch((0, template_1.parseTemplate)(this.tokenUrl).expand(templateVariables), {
|
|
134
|
+
headers: tokenRequestHeaders,
|
|
135
|
+
body: this.encodeBody(tokenRequestPayload, this.requestContentType),
|
|
136
|
+
method: 'POST',
|
|
137
|
+
});
|
|
146
138
|
if (fetchResult.status !== 200) {
|
|
147
|
-
res.
|
|
139
|
+
res.setHeader('Content-Type', 'text/html');
|
|
140
|
+
res.send(exports.HTML_ERROR_MSG);
|
|
148
141
|
}
|
|
149
142
|
const response = await fetchResult.json();
|
|
150
|
-
|
|
143
|
+
this.oauth2Response = {
|
|
151
144
|
accessToken: response.access_token,
|
|
152
145
|
refreshToken: response.refresh_token,
|
|
153
146
|
};
|
|
154
|
-
res.
|
|
147
|
+
res.setHeader('Content-Type', 'text/html');
|
|
148
|
+
res.send(exports.HTML_SUCCESS_MSG);
|
|
155
149
|
}
|
|
156
150
|
catch (error) {
|
|
157
|
-
res.
|
|
151
|
+
res.setHeader('Content-Type', 'text/html');
|
|
152
|
+
res.send(exports.HTML_ERROR_MSG);
|
|
158
153
|
}
|
|
159
154
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
155
|
+
/**
|
|
156
|
+
* Waits for the authorization code to be set.
|
|
157
|
+
* @returns A promise that resolves when the code is set.
|
|
158
|
+
*/
|
|
159
|
+
/* istanbul ignore next */
|
|
160
|
+
async callbackIsDone() {
|
|
161
|
+
if (this.oauth2Response) {
|
|
162
|
+
return this.oauth2Response;
|
|
163
163
|
}
|
|
164
|
+
else {
|
|
165
|
+
return new Promise(resolve => {
|
|
166
|
+
const interval = setInterval(() => {
|
|
167
|
+
if (this.oauth2Response) {
|
|
168
|
+
clearInterval(interval);
|
|
169
|
+
resolve(this.oauth2Response);
|
|
170
|
+
}
|
|
171
|
+
}, 100);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
encodeBody(bodyData, contentType) {
|
|
176
|
+
switch (contentType) {
|
|
177
|
+
case configurationTypes_1.RequestContentType.URL_ENCODED:
|
|
178
|
+
return new URLSearchParams(bodyData).toString();
|
|
179
|
+
case configurationTypes_1.RequestContentType.JSON:
|
|
180
|
+
return JSON.stringify(bodyData);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async updateToken(refreshToken) {
|
|
164
184
|
if (!Object.values(configurationTypes_1.RequestContentType).includes(this.requestContentType)) {
|
|
165
185
|
throw new errors_1.InvalidRequestContentTypeError(`Request content type not supported: ${this.requestContentType}`);
|
|
166
186
|
}
|
|
167
187
|
const bodyData = {
|
|
168
188
|
grant_type: 'refresh_token',
|
|
169
|
-
refresh_token:
|
|
189
|
+
refresh_token: refreshToken,
|
|
170
190
|
};
|
|
171
191
|
if (this.clientId) {
|
|
172
192
|
bodyData.client_id = this.clientId;
|
|
@@ -176,7 +196,7 @@ class OAuth2Helper {
|
|
|
176
196
|
}
|
|
177
197
|
const fetchOptions = {
|
|
178
198
|
headers: {
|
|
179
|
-
'Content-
|
|
199
|
+
'Content-Type': this.requestContentType,
|
|
180
200
|
...(this.refreshRequestParameters?.header ?? {}),
|
|
181
201
|
},
|
|
182
202
|
body: this.encodeBody(bodyData, this.requestContentType),
|
|
@@ -212,11 +232,9 @@ class OAuth2Helper {
|
|
|
212
232
|
app.get('/health', (_req, res) => {
|
|
213
233
|
res.send('pong');
|
|
214
234
|
});
|
|
215
|
-
app.get('/
|
|
216
|
-
app.post('/credentials/new/oauth2/callback', this.handleCallback);
|
|
217
|
-
app.get('/credentials/new/oauth2/token', this.handleToken);
|
|
235
|
+
app.get('/oauth2/callback', this.handleCallback);
|
|
218
236
|
this.server = app.listen(PORT, () => {
|
|
219
|
-
console.log(`Listening at port ${PORT}`);
|
|
237
|
+
console.log(` Listening at port ${PORT}`);
|
|
220
238
|
});
|
|
221
239
|
return this.serverUrl;
|
|
222
240
|
}
|
|
@@ -224,32 +242,13 @@ class OAuth2Helper {
|
|
|
224
242
|
* Stops the Express server.
|
|
225
243
|
*/
|
|
226
244
|
/* istanbul ignore next */
|
|
227
|
-
stopServer() {
|
|
245
|
+
async stopServer() {
|
|
228
246
|
if (this.server) {
|
|
247
|
+
await ngrok_1.default.kill();
|
|
229
248
|
this.server.close(() => {
|
|
230
|
-
console.log('Server has stopped');
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Waits for the authorization code to be set.
|
|
236
|
-
* @returns A promise that resolves when the code is set.
|
|
237
|
-
*/
|
|
238
|
-
/* istanbul ignore next */
|
|
239
|
-
async callbackIsDone() {
|
|
240
|
-
if (this.code) {
|
|
241
|
-
return true;
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
return new Promise(resolve => {
|
|
245
|
-
const interval = setInterval(() => {
|
|
246
|
-
if (this.code) {
|
|
247
|
-
clearInterval(interval);
|
|
248
|
-
resolve(true);
|
|
249
|
-
}
|
|
250
|
-
}, 100);
|
|
249
|
+
console.log(' Server has stopped');
|
|
251
250
|
});
|
|
252
251
|
}
|
|
253
252
|
}
|
|
254
253
|
}
|
|
255
|
-
exports.default =
|
|
254
|
+
exports.default = OAuth2Service;
|
|
@@ -9,6 +9,7 @@ const IntegrationsPlatform = tslib_1.__importStar(require("../../src/services/in
|
|
|
9
9
|
const IntegrationsPlatformResource = tslib_1.__importStar(require("../../src/resources/integrationsPlatform"));
|
|
10
10
|
const ConfigurationResource = tslib_1.__importStar(require("../../src/resources/configuration"));
|
|
11
11
|
const IntegrationResource = tslib_1.__importStar(require("../../src/resources/integrations"));
|
|
12
|
+
const CredentialResource = tslib_1.__importStar(require("../../src/resources/credentials"));
|
|
12
13
|
describe('Dev', () => {
|
|
13
14
|
const debuggerConfiguration = {
|
|
14
15
|
...IntegrationDebugger.getDefaultConfiguration(),
|
|
@@ -164,4 +165,24 @@ describe('Dev', () => {
|
|
|
164
165
|
.it('overrides --webhook-acknowledge-relative-url', () => {
|
|
165
166
|
(0, test_1.expect)(spawnStub.getCall(0).args.at(1)).to.include('--webhook-acknowledge-relative-url=bar');
|
|
166
167
|
});
|
|
168
|
+
test_1.test
|
|
169
|
+
.stdout()
|
|
170
|
+
.stub(ConfigurationResource, 'getConfiguration', stub => stub.returns({}))
|
|
171
|
+
.stub(IntegrationResource, 'validateIsIntegrationDirectory', stub => stub.returns(true))
|
|
172
|
+
.stub(CredentialResource, 'fetchCredential', stub => stub.resolves({ payload: { from: 'credential' } }))
|
|
173
|
+
.command(['dev', '--credential-id=123'])
|
|
174
|
+
.it('credential-id', () => {
|
|
175
|
+
(0, test_1.expect)(spawnStub.getCall(0).args.at(1)).to.include('--credential-payload={"from":"credential"}');
|
|
176
|
+
(0, test_1.expect)(spawnStub.getCall(0).args.at(1)).to.include('--read-only');
|
|
177
|
+
});
|
|
178
|
+
test_1.test
|
|
179
|
+
.stdout()
|
|
180
|
+
.stub(ConfigurationResource, 'getConfiguration', stub => stub.returns({}))
|
|
181
|
+
.stub(IntegrationResource, 'validateIsIntegrationDirectory', stub => stub.returns(true))
|
|
182
|
+
.stub(CredentialResource, 'fetchCredential', stub => stub.resolves({ payload: { from: 'credential' } }))
|
|
183
|
+
.command(['dev', '--credential-id=123', '--no-read-only'])
|
|
184
|
+
.it('credential-id && read-only', () => {
|
|
185
|
+
(0, test_1.expect)(spawnStub.getCall(0).args.at(1)).to.include('--credential-payload={"from":"credential"}');
|
|
186
|
+
(0, test_1.expect)(spawnStub.getCall(0).args.at(1)).to.not.include('--read-only');
|
|
187
|
+
});
|
|
167
188
|
});
|
|
@@ -4,7 +4,7 @@ const tslib_1 = require("tslib");
|
|
|
4
4
|
const test_1 = require("@oclif/test");
|
|
5
5
|
const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
|
|
6
6
|
const sinon = tslib_1.__importStar(require("sinon"));
|
|
7
|
-
const
|
|
7
|
+
const IntegrationResource = tslib_1.__importStar(require("../../src/resources/integrations"));
|
|
8
8
|
const Configuration = tslib_1.__importStar(require("../../src/resources/configuration"));
|
|
9
9
|
describe('init', () => {
|
|
10
10
|
beforeEach(() => {
|
|
@@ -17,7 +17,7 @@ describe('init', () => {
|
|
|
17
17
|
});
|
|
18
18
|
test_1.test
|
|
19
19
|
.stub(inquirer_1.default, 'prompt', stub => stub.resolves({ name: 'myIntegration' }))
|
|
20
|
-
.stub(
|
|
20
|
+
.stub(IntegrationResource, 'copyBoilerplate', stub => stub.returns('/my/super/path/myIntegration'))
|
|
21
21
|
.stub(process, 'chdir', stub => stub.returns(''))
|
|
22
22
|
.stdout()
|
|
23
23
|
.command(['init'])
|
|
@@ -25,7 +25,7 @@ describe('init', () => {
|
|
|
25
25
|
(0, test_1.expect)(ctx.stdout).to.contain('Your integration is available at');
|
|
26
26
|
});
|
|
27
27
|
test_1.test
|
|
28
|
-
.stub(
|
|
28
|
+
.stub(IntegrationResource, 'copyBoilerplate', stub => stub.returns('/my/super/path/myIntegration'))
|
|
29
29
|
.stub(process, 'chdir', stub => stub.returns(''))
|
|
30
30
|
.stdout()
|
|
31
31
|
.command(['init', '-n', 'myIntegration'])
|
|
@@ -33,7 +33,7 @@ describe('init', () => {
|
|
|
33
33
|
(0, test_1.expect)(ctx.stdout).to.contain('Your integration is available at');
|
|
34
34
|
});
|
|
35
35
|
test_1.test
|
|
36
|
-
.stub(
|
|
36
|
+
.stub(IntegrationResource, 'copyBoilerplate', stub => stub.throws(new Error('boom!')))
|
|
37
37
|
.stdout()
|
|
38
38
|
.command(['init', '-n', 'myIntegration'])
|
|
39
39
|
.catch(ctx => {
|