@sap/cli-core 2025.20.0 → 2025.24.0
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/CHANGELOG.md +10 -0
- package/README.md +1 -1
- package/commands/config.command/secrets.command/check.command.js +0 -1
- package/commands/config.command/secrets.command/refresh.command.js +0 -1
- package/commands/config.command/secrets.command/reset.command.js +0 -2
- package/commands/config.command/secrets.command/show.command.js +0 -2
- package/commands/handler/authentication/index.js +3 -6
- package/commands/handler/authentication/oauth/index.js +0 -1
- package/commands/handler/authentication/passcode/function.js +1 -4
- package/commands/handler/authentication/passcode/index.js +0 -1
- package/commands/handler/force.js +1 -1
- package/commands/handler/next.js +0 -1
- package/config/core.js +0 -2
- package/index.js +0 -2
- package/package.json +6 -6
- package/commands/handler/authentication/technicalJWT/cf.d.ts +0 -4
- package/commands/handler/authentication/technicalJWT/cf.js +0 -131
- package/commands/handler/authentication/technicalJWT/exec.d.ts +0 -8
- package/commands/handler/authentication/technicalJWT/exec.js +0 -192
- package/commands/handler/authentication/technicalJWT/index.d.ts +0 -2
- package/commands/handler/authentication/technicalJWT/index.js +0 -11
- package/commands/handler/authentication/technicalJWT/types.d.ts +0 -2
- package/commands/handler/authentication/technicalJWT/types.js +0 -5
- package/commands/handler/authentication/technicalJWT/utils.d.ts +0 -1
- package/commands/handler/authentication/technicalJWT/utils.js +0 -64
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## 2025.23.0
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Support for Node v23 and v24
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Changed confirmation prompts to default to "no" instead of "yes" for safer handling of destructive operations.
|
|
17
|
+
|
|
8
18
|
## 2025.20.0
|
|
9
19
|
|
|
10
20
|
### Fixed
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Command-Line Interface (CLI) Core Module.
|
|
4
4
|
|
|
5
|
-
[](https://nodejs.org/dist/latest-v20.x/docs/api/#) [](https://nodejs.org/dist/latest-v21.x/docs/api/#) [](https://nodejs.org/dist/latest-v22.x/docs/api/#) [](https://badge.fury.io/js/@sap%2Fcli-core)
|
|
5
|
+
[](https://nodejs.org/dist/latest-v20.x/docs/api/#) [](https://nodejs.org/dist/latest-v21.x/docs/api/#) [](https://nodejs.org/dist/latest-v22.x/docs/api/#) [](https://nodejs.org/dist/latest-v23.x/docs/api/#) [](https://nodejs.org/dist/latest-v24.x/docs/api/#) [](https://badge.fury.io/js/@sap%2Fcli-core)
|
|
6
6
|
|
|
7
7
|
## Content
|
|
8
8
|
|
|
@@ -2,7 +2,6 @@ import { OPTION_SECRETS_FILE } from "../../../constants.js";
|
|
|
2
2
|
import { createNextHandler, createOptionsHandler, createParseArgumentsHandler, } from "../../handler/index.js";
|
|
3
3
|
import { readSecretsFile } from "../../handler/authentication/oauth/secretsProvider/file.js";
|
|
4
4
|
import { get } from "../../../logger/index.js";
|
|
5
|
-
/* jscpd:ignore-end */
|
|
6
5
|
const create = () => async () => async () => {
|
|
7
6
|
const { output } = get("commands.secrets.check");
|
|
8
7
|
await readSecretsFile();
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { OPTION_HOST } from "../../../constants.js";
|
|
2
2
|
import { createNextHandler, createOptionsHandler, createParseArgumentsHandler, createResilientHandler, } from "../../handler/index.js";
|
|
3
3
|
import { create as createRefreshTokenHandler } from "../../handler/authentication/oauth/tokenProvider/refreshToken.js";
|
|
4
|
-
/* jscpd:ignore-end */
|
|
5
4
|
const refreshCommand = {
|
|
6
5
|
type: "command",
|
|
7
6
|
command: "refresh",
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
/* jscpd:ignore-start */
|
|
2
1
|
import { SecretsStorageSingleton } from "../../../cache/secrets/SecretsStorageSingleton.js";
|
|
3
2
|
import { createNextHandler, createParseArgumentsHandler, createResilientHandler, } from "../../handler/index.js";
|
|
4
3
|
import { create as createSecretsFromCacheProvider } from "../../handler/authentication/oauth/secretsProvider/cache.js";
|
|
5
|
-
/* jscpd:ignore-end */
|
|
6
4
|
const removeSecrets = async () => async () => {
|
|
7
5
|
await SecretsStorageSingleton.SINGLETON.deleteAllSecrets();
|
|
8
6
|
};
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
/* jscpd:ignore-start */
|
|
2
1
|
import { SecretsStorageSingleton } from "../../../cache/secrets/SecretsStorageSingleton.js";
|
|
3
2
|
import { get as getLogger } from "../../../logger/index.js";
|
|
4
3
|
import { createNextHandler, createParseArgumentsHandler, createResilientHandler, } from "../../handler/index.js";
|
|
5
4
|
import { create as createSecretsFromCacheProvider } from "../../handler/authentication/oauth/secretsProvider/cache.js";
|
|
6
|
-
/* jscpd:ignore-end */
|
|
7
5
|
const showSecrets = async () => async () => {
|
|
8
6
|
const { output, error } = getLogger("commands.secrets-show");
|
|
9
7
|
try {
|
|
@@ -6,13 +6,10 @@ import { create as createCacheSecretsProvider } from "./oauth/secretsProvider/ca
|
|
|
6
6
|
import { create as createRefreshTokenHandler } from "./oauth/tokenProvider/refreshToken.js";
|
|
7
7
|
import { create as createSetAuthorizationHandler } from "./oauth/tokenProvider/setAuthorization.js";
|
|
8
8
|
import { create as createPasscodeHandler } from "./passcode/index.js";
|
|
9
|
-
import { create as createTechnicalJWTHandler } from "./technicalJWT/index.js";
|
|
10
9
|
import { create as createOptionsHandler } from "../options/index.js";
|
|
11
10
|
import { AuthenticationMethod, OPTION_ACCESS_TOKEN, OPTION_AUTHORIZATION_FLOW, OPTION_AUTHORIZATION_URL, OPTION_CLIENT_ID, OPTION_CLIENT_SECRET, OPTION_CODE, OPTION_PASSCODE, OPTION_REFRESH_TOKEN, OPTION_SECRETS_FILE, OPTION_TOKEN_URL, } from "../../../constants.js";
|
|
12
11
|
import { getAuthenticationMethods } from "../../../config/core.js";
|
|
13
12
|
import { create as createSucceedHandler } from "../succeed.js";
|
|
14
|
-
export const create =
|
|
15
|
-
?
|
|
16
|
-
: ()
|
|
17
|
-
? createOptionsHandler(OPTION_PASSCODE)
|
|
18
|
-
: createSucceedHandler(), createOptionsHandler(OPTION_SECRETS_FILE), createOrHandler("commands.handler.authentication$handler", createSetAuthorizationHandler(), createPasscodeHandler(), createOauthHandler()));
|
|
13
|
+
export const create = () => createNextHandler("commands.handler.authentication", createResilientHandler(createNextHandler("commands.handler.authentication$oauth", createCacheSecretsProvider(), createRefreshTokenHandler())), createOptionsHandler(OPTION_CLIENT_ID), createOptionsHandler(OPTION_CLIENT_SECRET), createOptionsHandler(OPTION_ACCESS_TOKEN), createOptionsHandler(OPTION_REFRESH_TOKEN), createOptionsHandler(OPTION_CODE), createOptionsHandler(OPTION_TOKEN_URL), createOptionsHandler(OPTION_AUTHORIZATION_URL), createOptionsHandler(OPTION_AUTHORIZATION_FLOW), getAuthenticationMethods().includes(AuthenticationMethod.passcode)
|
|
14
|
+
? createOptionsHandler(OPTION_PASSCODE)
|
|
15
|
+
: createSucceedHandler(), createOptionsHandler(OPTION_SECRETS_FILE), createOrHandler("commands.handler.authentication$handler", createSetAuthorizationHandler(), createPasscodeHandler(), createOauthHandler()));
|
|
@@ -6,7 +6,6 @@ import { create as createTokenProvider } from "./tokenProvider/index.js";
|
|
|
6
6
|
import { create as createCheckOptionsExistence } from "../../checkOptionsExistence.js";
|
|
7
7
|
import { AuthenticationMethod, OPTION_PASSCODE, } from "../../../../constants.js";
|
|
8
8
|
import { getAuthenticationMethods } from "../../../../config/core.js";
|
|
9
|
-
/* jscpd:ignore-end */
|
|
10
9
|
export const create = () => {
|
|
11
10
|
if (getAuthenticationMethods().includes(AuthenticationMethod.oauth)) {
|
|
12
11
|
return createErrorHandler("failed to handle OAuth authorization", createNextHandler("commands.handler.authentication.oauth", createCheckOptionsExistence(OPTION_PASSCODE), createSecretsProvider(), createTokenProvider()));
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { set, get } from "../../../../config/index.js";
|
|
2
|
-
import { CONFIG_PASSCODE_FUNCTION, OPTION_PASSCODE,
|
|
3
|
-
/* jscpd:ignore-start */
|
|
4
|
-
} from "../../../../constants.js";
|
|
2
|
+
import { CONFIG_PASSCODE_FUNCTION, OPTION_PASSCODE, } from "../../../../constants.js";
|
|
5
3
|
export const create = () => async () => async () => {
|
|
6
4
|
const config = get();
|
|
7
|
-
/* jscpd:ignore-end */
|
|
8
5
|
if (!config[CONFIG_PASSCODE_FUNCTION] ||
|
|
9
6
|
typeof config[CONFIG_PASSCODE_FUNCTION] !== "function") {
|
|
10
7
|
throw new Error("passcode function not available from configuration or provided argument is no function");
|
|
@@ -7,7 +7,6 @@ import { create as createFunctionHandler } from "./function.js";
|
|
|
7
7
|
import { create as createSetPasscodeHandler } from "./setPasscode.js";
|
|
8
8
|
import { AuthenticationMethod, OPTION_ACCESS_TOKEN, OPTION_CLIENT_ID, OPTION_SECRETS_FILE, } from "../../../../constants.js";
|
|
9
9
|
import { getAuthenticationMethods } from "../../../../config/core.js";
|
|
10
|
-
/* jscpd:ignore-end */
|
|
11
10
|
export const create = () => {
|
|
12
11
|
if (getAuthenticationMethods().includes(AuthenticationMethod.passcode)) {
|
|
13
12
|
return createNextHandler("commands.handler.authentication.passcode", createCheckOptionsExistence(OPTION_CLIENT_ID), createCheckOptionsExistence(OPTION_ACCESS_TOKEN), createCheckOptionsExistence(OPTION_SECRETS_FILE), createOrHandler("commands.handler.authentication.passcode$value", createFunctionHandler(), createInputHandler()), createSetPasscodeHandler());
|
package/commands/handler/next.js
CHANGED
package/config/core.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
/* jscpd:ignore-start */
|
|
2
1
|
import { CLI_DEPRECATED, CLI_DEPRECATION_MESSAGE, CLI_DESCRIPTION, CLI_DISCOVERY_PATHS, CLI_GENERIC_OPTIONS_HELP, CLI_NAME, CLI_PACKAGE_NAME, CLI_SAP_HELP, CLI_SUPPORTED_AUTHENTICATION_METHODS, CLI_VERSION, } from "../constants.js";
|
|
3
|
-
/* jscpd:ignore-end */
|
|
4
2
|
import { get } from "./index.js";
|
|
5
3
|
export const getName = () => get()[CLI_NAME];
|
|
6
4
|
export const getPackageName = () => get()[CLI_PACKAGE_NAME];
|
package/index.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import * as dotenv from "dotenv";
|
|
2
2
|
import { configureLoggers } from "./configureLoggers.js";
|
|
3
3
|
import { set } from "./config/index.js";
|
|
4
|
-
/* jscpd:ignore-start */
|
|
5
4
|
import { AuthenticationMethod, CLI_DEPRECATED, CLI_DEPRECATION_MESSAGE, CLI_DESCRIPTION, CLI_DISCOVERY_PATHS, CLI_GENERIC_OPTIONS_HELP, CLI_NAME, CLI_PACKAGE_NAME, CLI_SAP_HELP, CLI_SUPPORTED_AUTHENTICATION_METHODS, CLI_VERSION, } from "./constants.js";
|
|
6
|
-
/* jscpd:ignore-end */
|
|
7
5
|
export { run } from "./dwc/run.js";
|
|
8
6
|
export { configure, getCommands } from "./module.js";
|
|
9
7
|
export { AuthenticationMethod };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cli-core",
|
|
3
|
-
"version": "2025.
|
|
3
|
+
"version": "2025.24.0",
|
|
4
4
|
"description": "Command-Line Interface (CLI) Core Module",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "SAP SE",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"homepage": "https://www.sap.com",
|
|
9
9
|
"main": "index.js",
|
|
10
10
|
"engines": {
|
|
11
|
-
"node": "^20 || ^21 || ^22",
|
|
12
|
-
"npm": "^9 || ^10"
|
|
11
|
+
"node": "^20 || ^21 || ^22 || ^23 || ^24",
|
|
12
|
+
"npm": "^9 || ^10 || ^11"
|
|
13
13
|
},
|
|
14
14
|
"keywords": [
|
|
15
15
|
"cli",
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"ajv": "8.17.1",
|
|
22
|
-
"axios": "1.
|
|
22
|
+
"axios": "1.13.1",
|
|
23
23
|
"commander": "12.1.0",
|
|
24
24
|
"compare-versions": "6.1.1",
|
|
25
25
|
"config": "4.1.1",
|
|
26
|
-
"dotenv": "17.2.
|
|
26
|
+
"dotenv": "17.2.3",
|
|
27
27
|
"form-data": "4.0.4",
|
|
28
|
-
"fs-extra": "11.3.
|
|
28
|
+
"fs-extra": "11.3.2",
|
|
29
29
|
"https": "1.0.0",
|
|
30
30
|
"https-proxy-agent": "7.0.6",
|
|
31
31
|
"jszip": "3.10.1",
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export function searchAllApps(names: any, targetOrg: any, targetSpace: any): any;
|
|
2
|
-
export function getServiceKey(serviceName: any, serviceKeyName: any): Promise<any>;
|
|
3
|
-
export function getEnv(names: any, targetOrg: any, targetSpace: any): any;
|
|
4
|
-
export function getCurrentTarget(): any;
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import * as exec from "./exec.js";
|
|
2
|
-
var getResults = exec.getResults;
|
|
3
|
-
export function searchAllApps(names, targetOrg, targetSpace) {
|
|
4
|
-
var nextUrl = "/v3/apps?names=" + names.join(",");
|
|
5
|
-
var fnFetchPage = function () {
|
|
6
|
-
return exec.cf(["curl", nextUrl], getResults);
|
|
7
|
-
};
|
|
8
|
-
var res = [];
|
|
9
|
-
var fnSearchPage = function () {
|
|
10
|
-
return fnFetchPage().then((apps) => {
|
|
11
|
-
try {
|
|
12
|
-
apps = JSON.parse(apps);
|
|
13
|
-
}
|
|
14
|
-
catch (e) {
|
|
15
|
-
console.error("Failed to parse the following body:");
|
|
16
|
-
console.error(apps);
|
|
17
|
-
throw e;
|
|
18
|
-
}
|
|
19
|
-
if (apps.errors) {
|
|
20
|
-
console.error(apps.errors[0]);
|
|
21
|
-
throw apps.errors[0];
|
|
22
|
-
}
|
|
23
|
-
for (const curResource of apps.resources) {
|
|
24
|
-
var resIndex = names.indexOf(curResource.name);
|
|
25
|
-
if (resIndex > -1) {
|
|
26
|
-
res[resIndex] = curResource;
|
|
27
|
-
for (var x = 0; x < names.length; x++) {
|
|
28
|
-
if (!res[x]) {
|
|
29
|
-
break;
|
|
30
|
-
}
|
|
31
|
-
if (x === names.length - 1) {
|
|
32
|
-
return Promise.resolve();
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
if (apps.pagination &&
|
|
38
|
-
apps.pagination.next &&
|
|
39
|
-
apps.pagination.next.href) {
|
|
40
|
-
nextUrl = "/v3/" + apps.pagination.next.href.split("/v3/")[1];
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
return Promise.resolve();
|
|
44
|
-
}
|
|
45
|
-
return fnSearchPage();
|
|
46
|
-
});
|
|
47
|
-
};
|
|
48
|
-
return exec
|
|
49
|
-
.cf(["curl", "/v3/organizations?names=" + targetOrg], getResults)
|
|
50
|
-
.then((orgs) => {
|
|
51
|
-
orgs = JSON.parse(orgs);
|
|
52
|
-
var orgGuid = orgs.resources && orgs.resources[0] && orgs.resources[0].guid;
|
|
53
|
-
nextUrl = nextUrl + "&organization_guids=" + orgGuid;
|
|
54
|
-
return exec.cf([
|
|
55
|
-
"curl",
|
|
56
|
-
"/v3/spaces?organization_guids=" + orgGuid + "&names=" + targetSpace,
|
|
57
|
-
], getResults);
|
|
58
|
-
})
|
|
59
|
-
.then((spaces) => {
|
|
60
|
-
spaces = JSON.parse(spaces);
|
|
61
|
-
var spaceGuid = spaces.resources && spaces.resources[0] && spaces.resources[0].guid;
|
|
62
|
-
nextUrl = nextUrl + "&space_guids=" + spaceGuid;
|
|
63
|
-
})
|
|
64
|
-
.then(() => fnSearchPage())
|
|
65
|
-
.then(() => res);
|
|
66
|
-
}
|
|
67
|
-
export function getServiceKey(serviceName, serviceKeyName) {
|
|
68
|
-
function resourceFromPagination(cfResult) {
|
|
69
|
-
if (!cfResult || cfResult.length === 0) {
|
|
70
|
-
return [];
|
|
71
|
-
}
|
|
72
|
-
const oResult = JSON.parse(cfResult);
|
|
73
|
-
if (oResult.pagination["total_results"] > 0) {
|
|
74
|
-
return oResult.resources;
|
|
75
|
-
}
|
|
76
|
-
return [];
|
|
77
|
-
}
|
|
78
|
-
return Promise.all([
|
|
79
|
-
exec
|
|
80
|
-
.cf(["curl", `/v3/service_instances?names=${serviceName}`], getResults)
|
|
81
|
-
.then((result) => resourceFromPagination(result))
|
|
82
|
-
.catch((err) => console.error(err.message)),
|
|
83
|
-
exec
|
|
84
|
-
.cf(["curl", `/v3/service_credential_bindings?names=${serviceKeyName}`], getResults)
|
|
85
|
-
.then((result) => resourceFromPagination(result))
|
|
86
|
-
.catch((err) => console.error(err.message)),
|
|
87
|
-
])
|
|
88
|
-
.then((result) => {
|
|
89
|
-
var serviceGuid = result[0][0].guid;
|
|
90
|
-
var credKeys = result[1];
|
|
91
|
-
if (!serviceGuid || credKeys.length === 0) {
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
var serviceKey = credKeys.filter((key) => key.relationships["service_instance"].data.guid === serviceGuid);
|
|
95
|
-
if (serviceKey.length === 0) {
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
return exec
|
|
99
|
-
.cf([
|
|
100
|
-
"curl",
|
|
101
|
-
`/v3/service_credential_bindings/${serviceKey[0].guid}/details`,
|
|
102
|
-
], getResults)
|
|
103
|
-
.then((details) => JSON.parse(details));
|
|
104
|
-
})
|
|
105
|
-
.catch((err) => console.error(`Failed to fetch service key ${serviceKeyName}. ${err}`));
|
|
106
|
-
}
|
|
107
|
-
export function getEnv(names, targetOrg, targetSpace) {
|
|
108
|
-
if (!Array.isArray(names)) {
|
|
109
|
-
names = [names];
|
|
110
|
-
}
|
|
111
|
-
return module.exports
|
|
112
|
-
.searchAllApps(names, targetOrg, targetSpace)
|
|
113
|
-
.then((apps) => Promise.all(apps.map((a) => exec.cf(["curl", `/v3/apps/${a.guid}/env`], getResults))));
|
|
114
|
-
}
|
|
115
|
-
export function getCurrentTarget() {
|
|
116
|
-
return exec.cf(["t"], getResults).then((result) => {
|
|
117
|
-
var json = result
|
|
118
|
-
.replace(/ /g, "")
|
|
119
|
-
.split("\n")
|
|
120
|
-
.map((l) => {
|
|
121
|
-
var i = l.indexOf(":");
|
|
122
|
-
if (i < 0) {
|
|
123
|
-
return l;
|
|
124
|
-
}
|
|
125
|
-
return '"' + l.substring(0, i) + '":"' + l.substring(i + 1) + '"';
|
|
126
|
-
});
|
|
127
|
-
json.pop();
|
|
128
|
-
json = "{" + json.join(",\n") + "}";
|
|
129
|
-
return JSON.parse(json);
|
|
130
|
-
});
|
|
131
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export function exec(cmd: any, args: any, options: any): Promise<any>;
|
|
2
|
-
export function kill(): Promise<any[]>;
|
|
3
|
-
export namespace getResults {
|
|
4
|
-
let stdio: (string | number)[];
|
|
5
|
-
}
|
|
6
|
-
export function cf(args: any, options: any): any;
|
|
7
|
-
export function npm(args: any, options: any): any;
|
|
8
|
-
export function git(args: any, options: any): Promise<any>;
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import * as jsZip from "jszip";
|
|
2
|
-
import * as childProcess from "child_process";
|
|
3
|
-
import * as path from "path";
|
|
4
|
-
import * as http from "https";
|
|
5
|
-
import * as fs from "fs-extra";
|
|
6
|
-
process.on("message", (m) => {
|
|
7
|
-
if (m.kill) {
|
|
8
|
-
module.exports.kill().then(() => {
|
|
9
|
-
process.send({ killed: true });
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
// Define exec
|
|
14
|
-
var processes = {};
|
|
15
|
-
var killCallbacks = {};
|
|
16
|
-
export var exec = function (cmd, args, options) {
|
|
17
|
-
options = options || {};
|
|
18
|
-
options.stdio = options.stdio || [0, 1, 2];
|
|
19
|
-
options.cwd = options.cwd || path.resolve(import.meta.dirname);
|
|
20
|
-
console.log("Executing command: " + cmd + " " + args.join(" "));
|
|
21
|
-
return new Promise((resolve, reject) => {
|
|
22
|
-
var output = [];
|
|
23
|
-
var proc = childProcess.spawn(cmd, args, options);
|
|
24
|
-
processes[proc.pid] = proc;
|
|
25
|
-
if (proc.stdout) {
|
|
26
|
-
proc.stdout.on("data", (chunk) => {
|
|
27
|
-
output.push(`${chunk}`);
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
proc.on("error", reject);
|
|
31
|
-
proc.on("exit", (code, signal) => {
|
|
32
|
-
if (processes[proc.pid] === proc) {
|
|
33
|
-
delete processes[proc.pid];
|
|
34
|
-
}
|
|
35
|
-
if (typeof killCallbacks[proc.pid] === "function") {
|
|
36
|
-
console.log("Killed process: " + proc.spawnargs.join(" "));
|
|
37
|
-
return killCallbacks[proc.pid]();
|
|
38
|
-
}
|
|
39
|
-
if (code !== 0) {
|
|
40
|
-
return reject(new Error("Failed command: " +
|
|
41
|
-
cmd +
|
|
42
|
-
" " +
|
|
43
|
-
args.join(" ") +
|
|
44
|
-
` (exited ${code})`));
|
|
45
|
-
}
|
|
46
|
-
return resolve(output.join(""));
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
};
|
|
50
|
-
export var kill = function () {
|
|
51
|
-
var pids = Object.keys(processes);
|
|
52
|
-
var proms = [];
|
|
53
|
-
for (const pid of pids) {
|
|
54
|
-
var curProc = processes[pid];
|
|
55
|
-
if (!curProc.iskilled) {
|
|
56
|
-
proms.push(
|
|
57
|
-
// NOSONAR
|
|
58
|
-
new Promise((resolve) => {
|
|
59
|
-
setTimeout(resolve, 30 * 1000);
|
|
60
|
-
killCallbacks[curProc.pid] = resolve;
|
|
61
|
-
console.log("Killing process: " + curProc.spawnargs.join(" "));
|
|
62
|
-
curProc.kill();
|
|
63
|
-
}));
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return Promise.all(proms);
|
|
67
|
-
};
|
|
68
|
-
export const getResults = { stdio: [0, "pipe", 2] };
|
|
69
|
-
// Define cf
|
|
70
|
-
var cfReady;
|
|
71
|
-
var cfCmd = "cf";
|
|
72
|
-
process.env.CF_DEBUG = "1";
|
|
73
|
-
var internalMessaging = process.env.CF_DEBUG === "1" ? "inherit" : "ignore";
|
|
74
|
-
var checkCF = function () {
|
|
75
|
-
var win = process.platform === "win32";
|
|
76
|
-
var mac = process.platform === "darwin";
|
|
77
|
-
var plat = win ? "windows64-exe" : mac ? "macosx64-binary" : "linux64-binary";
|
|
78
|
-
var tmpFolder = path.resolve(import.meta.dirname, "../.tmp/cf");
|
|
79
|
-
var cfZipFile = path.resolve(tmpFolder, "client.zip");
|
|
80
|
-
var cfExeFile = path.resolve(tmpFolder, "cf.exe");
|
|
81
|
-
var cfBinFile = path.resolve(tmpFolder, "cf");
|
|
82
|
-
var downloadLink = `https://packages.cloudfoundry.org/stable?release=${plat}&source=github`;
|
|
83
|
-
if (cfReady) {
|
|
84
|
-
return cfReady;
|
|
85
|
-
}
|
|
86
|
-
cfReady = exec("cf", ["version"], { stdio: internalMessaging })
|
|
87
|
-
.then(() => {
|
|
88
|
-
console.log("Using global cf client");
|
|
89
|
-
cfCmd = "cf";
|
|
90
|
-
})
|
|
91
|
-
.catch(() => {
|
|
92
|
-
// TODO: enable local cf client after downloading
|
|
93
|
-
return (fs
|
|
94
|
-
.ensureDir(tmpFolder)
|
|
95
|
-
.then(() => fs.exists(cfZipFile))
|
|
96
|
-
.then((exists) => {
|
|
97
|
-
if (exists) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
return new Promise((resolve, reject) => {
|
|
101
|
-
console.log(`Downloading cf client for platform ${plat}`);
|
|
102
|
-
console.log(downloadLink);
|
|
103
|
-
var file = fs.createWriteStream(cfZipFile, { mode: 777 });
|
|
104
|
-
var fnDownload = function (url) {
|
|
105
|
-
http.get(url, (res, err) => {
|
|
106
|
-
if (err) {
|
|
107
|
-
return reject(err);
|
|
108
|
-
}
|
|
109
|
-
if (res.statusCode === 302 &&
|
|
110
|
-
res.headers &&
|
|
111
|
-
res.headers.location) {
|
|
112
|
-
return fnDownload(res.headers.location);
|
|
113
|
-
}
|
|
114
|
-
res.pipe(file).on("finish", resolve);
|
|
115
|
-
});
|
|
116
|
-
};
|
|
117
|
-
fnDownload(downloadLink);
|
|
118
|
-
}).then(() => console.log("Downloading cf client Finished ..."));
|
|
119
|
-
})
|
|
120
|
-
// Unzip
|
|
121
|
-
.then(() => {
|
|
122
|
-
if (win) {
|
|
123
|
-
console.log("Unzipping cf client");
|
|
124
|
-
return fs
|
|
125
|
-
.readFile(cfZipFile)
|
|
126
|
-
.then((data) => {
|
|
127
|
-
// NOSONAR we're dealing with cf and we're pretty sure nobody messes it up
|
|
128
|
-
return jsZip.loadAsync(data);
|
|
129
|
-
})
|
|
130
|
-
.then((zip) => {
|
|
131
|
-
var prom = [];
|
|
132
|
-
Object.keys(zip.files).forEach((filename) => {
|
|
133
|
-
if (zip.files[filename].dir) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
prom.push(zip.files[filename]
|
|
137
|
-
.async("nodebuffer")
|
|
138
|
-
.then((fileData) => {
|
|
139
|
-
let dest = path.resolve(tmpFolder, filename);
|
|
140
|
-
return fs.outputFile(dest, fileData);
|
|
141
|
-
}));
|
|
142
|
-
});
|
|
143
|
-
return Promise.all(prom)
|
|
144
|
-
.then(() => console.log("Unzipping cf client finished"))
|
|
145
|
-
.then(() => {
|
|
146
|
-
cfCmd = cfExeFile;
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
// Could be done with node as well
|
|
152
|
-
// TODO: test for linux and mac os
|
|
153
|
-
console.log("Unzipping cf client");
|
|
154
|
-
console.log(cfZipFile);
|
|
155
|
-
return exec("tar", ["-xf", cfZipFile, "-C", tmpFolder])
|
|
156
|
-
.then(() => console.log("Unzipping cf client finished"))
|
|
157
|
-
.then(() => {
|
|
158
|
-
cfCmd = cfBinFile;
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
}));
|
|
162
|
-
})
|
|
163
|
-
.then(() => {
|
|
164
|
-
// This is expected
|
|
165
|
-
});
|
|
166
|
-
return cfReady;
|
|
167
|
-
};
|
|
168
|
-
var execCF = function (args, options) {
|
|
169
|
-
return checkCF().then(() => exec(cfCmd, args, options));
|
|
170
|
-
};
|
|
171
|
-
export const cf = execCF;
|
|
172
|
-
// Define npm
|
|
173
|
-
var execNpm = function (args, options) {
|
|
174
|
-
if (typeof args === "string") {
|
|
175
|
-
return execNpmRun(args);
|
|
176
|
-
}
|
|
177
|
-
var cmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
178
|
-
options = options || {};
|
|
179
|
-
options.cwd = options.cwd || path.resolve(import.meta.dirname, "..");
|
|
180
|
-
return exec(cmd, args, options);
|
|
181
|
-
};
|
|
182
|
-
var execNpmRun = function (script) {
|
|
183
|
-
return execNpm(["run", script], { cwd: path.resolve(import.meta.dirname, "..") });
|
|
184
|
-
};
|
|
185
|
-
export const npm = execNpm;
|
|
186
|
-
// Define git
|
|
187
|
-
var execGit = function (args, options) {
|
|
188
|
-
options = options || {};
|
|
189
|
-
options.stdio = options.stdio || ["pipe", 1, 2];
|
|
190
|
-
return exec("git", args, options);
|
|
191
|
-
};
|
|
192
|
-
export const git = execGit;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { set as setConfig } from "../../../../config/index.js";
|
|
2
|
-
import { buildOption } from "../../../../utils/commands.js";
|
|
3
|
-
import { OPTION_SECRET } from "./types.js";
|
|
4
|
-
import { getTechnicalJwt } from "./utils.js";
|
|
5
|
-
export const create = () => async (command) => {
|
|
6
|
-
command.addOption(await buildOption(command, OPTION_SECRET));
|
|
7
|
-
return async () => {
|
|
8
|
-
const token = await getTechnicalJwt();
|
|
9
|
-
setConfig({ authorization: { Authorization: `Bearer ${token}` } });
|
|
10
|
-
};
|
|
11
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const getTechnicalJwt: () => Promise<string>;
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import { URLSearchParams } from "url";
|
|
3
|
-
import fs from "fs-extra";
|
|
4
|
-
// eslint-disable-next-line import/extensions
|
|
5
|
-
import * as cf from "./cf.js";
|
|
6
|
-
import { get as getConfig } from "../../../../config/index.js";
|
|
7
|
-
import { get } from "../../../../logger/index.js";
|
|
8
|
-
import { fetch } from "../../../../utils/http/index.js";
|
|
9
|
-
import { OPTION_SECRET } from "./types.js";
|
|
10
|
-
import { GrantType } from "../../../../types.js";
|
|
11
|
-
const getLogger = () => get("commands.handler.authentication.technicalJWT.utils");
|
|
12
|
-
const APPNAME_GLOBAL = "dwaas-core";
|
|
13
|
-
const getSecret = async () => {
|
|
14
|
-
const { trace } = getLogger();
|
|
15
|
-
const config = getConfig();
|
|
16
|
-
const secretsFile = config.options[OPTION_SECRET.longName] ||
|
|
17
|
-
path.join(process.cwd(), ".secret.json");
|
|
18
|
-
trace("reading secret from", secretsFile);
|
|
19
|
-
if (fs.existsSync(secretsFile)) {
|
|
20
|
-
const content = await fs.readFile(secretsFile, "utf8");
|
|
21
|
-
return JSON.parse(content);
|
|
22
|
-
}
|
|
23
|
-
const currentTarget = await cf.getCurrentTarget();
|
|
24
|
-
let [env] = await cf.getEnv(APPNAME_GLOBAL, currentTarget.org, currentTarget.space);
|
|
25
|
-
env = JSON.parse(env);
|
|
26
|
-
const vcap = Object.keys(env)
|
|
27
|
-
.map((k) => env[k].VCAP_SERVICES)
|
|
28
|
-
.find((v) => v);
|
|
29
|
-
const url = Object.keys(env)
|
|
30
|
-
.map((k) => env[k].VCAP_APPLICATION)
|
|
31
|
-
.find((v) => v && v.uris && v.uris[0]).uris[0];
|
|
32
|
-
if (!vcap || !vcap.xsuaa) {
|
|
33
|
-
throw new Error("The target application is missing a uaa binding.");
|
|
34
|
-
}
|
|
35
|
-
const uaa = vcap.xsuaa.find((v) => v.credentials);
|
|
36
|
-
if (!uaa) {
|
|
37
|
-
throw new Error("The target application is missing a uaa binding with credentials.");
|
|
38
|
-
}
|
|
39
|
-
return {
|
|
40
|
-
url: `https://${url}`,
|
|
41
|
-
uaaUrl: uaa.credentials.url,
|
|
42
|
-
clientid: uaa.credentials.clientid,
|
|
43
|
-
clientsecret: uaa.credentials.clientsecret,
|
|
44
|
-
tenantid: uaa.credentials.tenantid,
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
export const getTechnicalJwt = async () => {
|
|
48
|
-
const secret = await getSecret();
|
|
49
|
-
const { data } = await fetch({
|
|
50
|
-
method: "POST",
|
|
51
|
-
url: `${secret.uaaUrl}/oauth/token`,
|
|
52
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
53
|
-
data: new URLSearchParams({
|
|
54
|
-
grant_type: GrantType.client_credentials,
|
|
55
|
-
response_type: "token",
|
|
56
|
-
client_id: secret.clientid,
|
|
57
|
-
client_secret: secret.clientsecret,
|
|
58
|
-
}).toString(),
|
|
59
|
-
});
|
|
60
|
-
if (!data.access_token) {
|
|
61
|
-
throw new Error("No token could be retrieved from the application.");
|
|
62
|
-
}
|
|
63
|
-
return data.access_token;
|
|
64
|
-
};
|