@sap/cli-core 2024.22.0 → 2024.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 +71 -0
- package/README.md +1 -1
- package/cache/secrets/SecretsStorageImpl.js +8 -45
- package/cache/secrets/utils.d.ts +7 -1
- package/cache/secrets/utils.js +63 -5
- package/commands/config.command/secrets.command/check.command.d.ts +4 -0
- package/commands/config.command/secrets.command/check.command.js +23 -0
- package/commands/config.command/secrets.command/index.js +2 -1
- package/commands/handler/authentication/index.js +1 -1
- package/commands/handler/authentication/oauth/secretsProvider/file.d.ts +2 -1
- package/commands/handler/authentication/oauth/secretsProvider/file.js +25 -5
- package/commands/handler/authentication/oauth/secretsProvider/options.js +2 -0
- package/commands/handler/authentication/oauth/tokenProvider/getToken.js +12 -6
- package/commands/handler/authentication/oauth/tokenProvider/utils.js +13 -5
- package/commands/handler/authentication/oauth/utils.d.ts +5 -2
- package/commands/handler/authentication/oauth/utils.js +1 -0
- package/commands/handler/authentication/technicalJWT/utils.js +2 -1
- package/commands/handler/fetch/fetch.js +1 -1
- package/commands/handler/fetch/utils.d.ts +1 -1
- package/commands/handler/fetch/utils.js +1 -1
- package/commands/handler/input/input.js +6 -1
- package/commands/login.command.js +1 -0
- package/constants.d.ts +1 -0
- package/constants.js +14 -1
- package/package.json +3 -3
- package/types.d.ts +6 -0
- package/types.js +8 -1
- package/utils/utils.d.ts +1 -0
- package/utils/utils.js +14 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,77 @@ 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
|
+
## 2024.24.0
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Support for Node v22.
|
|
13
|
+
|
|
14
|
+
- New command `<cli> config secrets check` has been added to the CLI. This command allows you to verify whether the secrets file used to run commands is consistent.
|
|
15
|
+
|
|
16
|
+
Examples for inconsistent secrets files:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
$ datasphere config secrets check
|
|
20
|
+
✔ Please enter the path to the secrets file: … secrets.json
|
|
21
|
+
the provided secrets file is not consistent. the secrets file is missing either property access_token or properties refresh_token, client_id and client_secret
|
|
22
|
+
Failed to check secrets file consistency
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
$ datasphere config secrets check
|
|
27
|
+
✔ Please enter the path to the secrets file: … secrets.json
|
|
28
|
+
the provided secrets file is not consistent. the secrets file is missing either property tenantUrl or properties authorization_url and token_url
|
|
29
|
+
Failed to check secrets file consistency
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Example for consistent secrets file:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
$ datasphere config secrets check
|
|
36
|
+
✔ Please enter the path to the secrets file: … secrets.json
|
|
37
|
+
the secrets file is consistent
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- Support for the authorization flow `client_credentials`. When logging in, users can now explicitly use the `client_credentials` flow. The help shows the available option to specify the authorization flow.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
% datasphere login --help
|
|
44
|
+
Usage: datasphere login [options]
|
|
45
|
+
|
|
46
|
+
log in to your account using interactive OAuth authentication
|
|
47
|
+
|
|
48
|
+
Options:
|
|
49
|
+
-H, --host <host> specifies the url where the tenant is hosted (optional)
|
|
50
|
+
-A, --authorization-url <url> authorization url for interactive oauth session authentication (optional)
|
|
51
|
+
-t, --token-url <url> token url for interactive oauth session authentication (optional)
|
|
52
|
+
-c, --client-id <id> client id for interactive oauth session authentication (optional)
|
|
53
|
+
-C, --client-secret <secret> client secret for interactive oauth session authentication (optional)
|
|
54
|
+
-a, --access-token <token> access token for interactive oauth session authentication (optional)
|
|
55
|
+
-b, --code <code> code for oauth token retrieval (optional)
|
|
56
|
+
-r, --refresh-token <token> refresh token for interactive oauth session authentication (optional)
|
|
57
|
+
-s, --secrets-file <file> path to secrets file (optional)
|
|
58
|
+
-B, --browser <browser> specifies the browser to open (optional) (choices: "chrome", "firefox", "edge", default: "chrome")
|
|
59
|
+
-d, --authorization-flow <authorization-flow> specifies the authorization flow to use (optional) (choices: "authorization_code", "client_credentials", default: "authorization_code")
|
|
60
|
+
-h, --help display help for command
|
|
61
|
+
|
|
62
|
+
Only command-specific options are listed here. To learn more about available generic options, visit https://tinyurl.com/yck8vv4w
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
There's a new option `-d, --authorization-flow <authorization-flow> specifies the authorization flow to use (optional) (choices: "authorization_code", "client_credentials", default: "authorization_code")`.
|
|
66
|
+
|
|
67
|
+
By default, the authorization flow `authorization_code` is used which was also the implicit default before. If users create a technical OAuth client, they must set the authorization flow to `client_credentials` explicitly.
|
|
68
|
+
|
|
69
|
+
`% datasphere login --authorization-flow client_credentials ...`
|
|
70
|
+
|
|
71
|
+
- When using option `--secrets-file` and the file containing the secrets does not exist or is not in a valid JSON format, the CLI now shows an error to the user: `The secrets file at location /path/to/secrets.json could not be read and JSON.parse'd. Does the file exist and is it a valid JSON file?`
|
|
72
|
+
|
|
73
|
+
## 2024.22.0
|
|
74
|
+
|
|
75
|
+
### Fixed
|
|
76
|
+
|
|
77
|
+
- Issues with option `--input` which would not set the request body correctly. As a workaround, option `--file-path` can be used.
|
|
78
|
+
|
|
8
79
|
## 2024.17.0
|
|
9
80
|
|
|
10
81
|
### Added
|
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-v18.x/docs/api/#) [](https://nodejs.org/dist/latest-v19.x/docs/api/#) [](https://nodejs.org/dist/latest-v20.x/docs/api/#) [](https://nodejs.org/dist/latest-v21.x/docs/api/#) [](https://badge.fury.io/js/@sap%2Fcli-core)
|
|
5
|
+
[](https://nodejs.org/dist/latest-v18.x/docs/api/#) [](https://nodejs.org/dist/latest-v19.x/docs/api/#) [](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)
|
|
6
6
|
|
|
7
7
|
## Content
|
|
8
8
|
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SecretsStorageImpl = void 0;
|
|
4
|
-
const config_1 = require("../../config");
|
|
5
4
|
const constants_1 = require("../../constants");
|
|
6
5
|
const logger_1 = require("../../logger");
|
|
7
6
|
const utils_1 = require("../../logger/utils");
|
|
8
7
|
const types_1 = require("../../types");
|
|
9
|
-
const http_1 = require("../../utils/http");
|
|
10
|
-
const utils_2 = require("../../utils/utils");
|
|
11
8
|
const cache_1 = require("../cache");
|
|
12
|
-
const
|
|
9
|
+
const utils_2 = require("./utils");
|
|
13
10
|
class SecretsStorageImpl {
|
|
14
11
|
secrets = [];
|
|
15
12
|
logger;
|
|
@@ -24,7 +21,7 @@ class SecretsStorageImpl {
|
|
|
24
21
|
for (let i = 0; i < this.secrets.length; i++) {
|
|
25
22
|
this.secrets[i].id = i;
|
|
26
23
|
if (this.secrets[i].client_id) {
|
|
27
|
-
this.secrets[i].customClient = (0,
|
|
24
|
+
this.secrets[i].customClient = (0, utils_2.isCustomClient)(this.secrets[i].client_id);
|
|
28
25
|
// eslint-disable-next-line no-await-in-loop
|
|
29
26
|
await this.updateUrls(i);
|
|
30
27
|
}
|
|
@@ -52,7 +49,7 @@ class SecretsStorageImpl {
|
|
|
52
49
|
return this.getSecretById(await this.getDefaultSecretId());
|
|
53
50
|
}
|
|
54
51
|
async getDefaultSecretId() {
|
|
55
|
-
const tenantUrl = (0,
|
|
52
|
+
const tenantUrl = (0, utils_2.getTenantUrl)();
|
|
56
53
|
const { host } = new URL(tenantUrl);
|
|
57
54
|
const secret = this.secrets.find((s) => new URL(s.tenantUrl).host === host);
|
|
58
55
|
if (!secret) {
|
|
@@ -76,7 +73,7 @@ class SecretsStorageImpl {
|
|
|
76
73
|
this.secrets.splice(id, 1);
|
|
77
74
|
}
|
|
78
75
|
updateSecret(secret) {
|
|
79
|
-
const tenantUrl = (0,
|
|
76
|
+
const tenantUrl = (0, utils_2.getTenantUrl)();
|
|
80
77
|
let prevSecretIx = this.secrets.findIndex((s) => s.tenantUrl === tenantUrl);
|
|
81
78
|
let newSecret;
|
|
82
79
|
if (prevSecretIx > -1) {
|
|
@@ -96,42 +93,7 @@ class SecretsStorageImpl {
|
|
|
96
93
|
return prevSecretIx;
|
|
97
94
|
}
|
|
98
95
|
async updateUrls(secretIndex) {
|
|
99
|
-
|
|
100
|
-
if (secret.client_id) {
|
|
101
|
-
const config = (0, config_1.get)();
|
|
102
|
-
let oauth = {};
|
|
103
|
-
if (!secret.customClient &&
|
|
104
|
-
(!secret.authorization_url || !secret.token_url)) {
|
|
105
|
-
oauth = (await (0, http_1.fetch)({
|
|
106
|
-
method: "GET",
|
|
107
|
-
url: `${config.tenantUrl}/oauth`,
|
|
108
|
-
})).data;
|
|
109
|
-
}
|
|
110
|
-
let authorizationUrl = secret.authorization_url;
|
|
111
|
-
if (!authorizationUrl) {
|
|
112
|
-
if (!secret.customClient) {
|
|
113
|
-
authorizationUrl = oauth.authorizationUrl;
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
authorizationUrl = config.authorizationUrl;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
let tokenUrl = secret.token_url;
|
|
120
|
-
if (!tokenUrl) {
|
|
121
|
-
if (!secret.customClient) {
|
|
122
|
-
tokenUrl = oauth.tokenUrl;
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
tokenUrl = config.tokenUrl;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
if (!tokenUrl || !authorizationUrl) {
|
|
129
|
-
throw new Error("invalid token url or authorization url");
|
|
130
|
-
}
|
|
131
|
-
this.secrets[secretIndex].authorization_url =
|
|
132
|
-
(0, utils_2.removeQueryParametersFromUrl)(authorizationUrl);
|
|
133
|
-
this.secrets[secretIndex].token_url = (0, utils_2.removeQueryParametersFromUrl)(tokenUrl);
|
|
134
|
-
}
|
|
96
|
+
this.secrets[secretIndex] = await (0, utils_2.updateUrls)(this.secrets[secretIndex]);
|
|
135
97
|
}
|
|
136
98
|
async storeSecret(secret) {
|
|
137
99
|
this.updateSecret(secret);
|
|
@@ -143,8 +105,9 @@ class SecretsStorageImpl {
|
|
|
143
105
|
}
|
|
144
106
|
removeInconsistentSecrets() {
|
|
145
107
|
this.secrets = this.secrets.filter((secret) => {
|
|
146
|
-
|
|
147
|
-
|
|
108
|
+
const consistent = (0, utils_2.isSecretConsistent)(secret);
|
|
109
|
+
if (!consistent.consistent) {
|
|
110
|
+
this.logger.warn(`secret ${JSON.stringify(secret)} is not consistent and removed from cache. ${consistent.errors.join(". ")}`);
|
|
148
111
|
return false;
|
|
149
112
|
}
|
|
150
113
|
return true;
|
package/cache/secrets/utils.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { Secret } from "../../types";
|
|
2
2
|
export declare const isCustomClient: (clientId?: string) => boolean;
|
|
3
3
|
export declare const getTenantUrl: () => string;
|
|
4
|
-
export declare function
|
|
4
|
+
export declare function updateUrls(secret: Secret): Promise<Secret>;
|
|
5
|
+
export declare function isSecretConsistent(secret: Secret): {
|
|
6
|
+
consistent: true;
|
|
7
|
+
} | {
|
|
8
|
+
consistent: false;
|
|
9
|
+
errors: Array<string>;
|
|
10
|
+
};
|
package/cache/secrets/utils.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getTenantUrl = exports.isCustomClient = void 0;
|
|
4
|
+
exports.updateUrls = updateUrls;
|
|
4
5
|
exports.isSecretConsistent = isSecretConsistent;
|
|
5
6
|
const config_1 = require("../../config");
|
|
6
7
|
const constants_1 = require("../../constants");
|
|
8
|
+
const types_1 = require("../../types");
|
|
9
|
+
const http_1 = require("../../utils/http");
|
|
10
|
+
const utils_1 = require("../../utils/utils");
|
|
7
11
|
// Pre-delivered OAuth Client ID: 5a638330-5899-366e-ac00-ab62cc32dcda
|
|
8
12
|
// Custom OAuth Client ID: sb-00bb7bc2-cc32-423c-921c-2abdee11a29d!b49931|client!b3650
|
|
9
13
|
const isCustomClient = (clientId) => clientId ? clientId.startsWith("sb-") : true;
|
|
@@ -17,10 +21,64 @@ const getTenantUrl = () => {
|
|
|
17
21
|
return tenantUrl;
|
|
18
22
|
};
|
|
19
23
|
exports.getTenantUrl = getTenantUrl;
|
|
24
|
+
async function updateUrls(secret) {
|
|
25
|
+
if (secret.client_id) {
|
|
26
|
+
const config = (0, config_1.get)();
|
|
27
|
+
let oauth = {};
|
|
28
|
+
if (!secret.customClient &&
|
|
29
|
+
(!secret.authorization_url || !secret.token_url)) {
|
|
30
|
+
oauth = (await (0, http_1.fetch)({
|
|
31
|
+
method: "GET",
|
|
32
|
+
url: `${config.tenantUrl}/oauth`,
|
|
33
|
+
})).data;
|
|
34
|
+
}
|
|
35
|
+
let authorizationUrl = secret.authorization_url;
|
|
36
|
+
if (!authorizationUrl) {
|
|
37
|
+
if (!secret.customClient) {
|
|
38
|
+
authorizationUrl = oauth.authorizationUrl;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
authorizationUrl = config.authorizationUrl;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
let tokenUrl = secret.token_url;
|
|
45
|
+
if (!tokenUrl) {
|
|
46
|
+
if (!secret.customClient) {
|
|
47
|
+
tokenUrl = oauth.tokenUrl;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
tokenUrl = config.tokenUrl;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (!tokenUrl || !authorizationUrl) {
|
|
54
|
+
throw new Error("invalid token url or authorization url");
|
|
55
|
+
}
|
|
56
|
+
// eslint-disable-next-line no-param-reassign
|
|
57
|
+
secret.authorization_url = (0, utils_1.removeQueryParametersFromUrl)(authorizationUrl);
|
|
58
|
+
// eslint-disable-next-line no-param-reassign
|
|
59
|
+
secret.token_url = (0, utils_1.removeQueryParametersFromUrl)(tokenUrl);
|
|
60
|
+
}
|
|
61
|
+
return secret;
|
|
62
|
+
}
|
|
20
63
|
function isSecretConsistent(secret) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
(
|
|
24
|
-
|
|
25
|
-
(
|
|
64
|
+
const errors = [];
|
|
65
|
+
if (!((0, utils_1.isValidURL)(secret.tenantUrl) ||
|
|
66
|
+
((secret.authorization_flow !== types_1.GrantType.authorization_code ||
|
|
67
|
+
(0, utils_1.isValidURL)(secret.authorization_url)) &&
|
|
68
|
+
(0, utils_1.isValidURL)(secret.token_url)))) {
|
|
69
|
+
errors.push("the secrets file is missing either property tenantUrl or properties authorization_url and token_url" +
|
|
70
|
+
" or the URLs are not valid");
|
|
71
|
+
}
|
|
72
|
+
if (!(secret.access_token ||
|
|
73
|
+
((secret.authorization_flow !== types_1.GrantType.authorization_code ||
|
|
74
|
+
secret.refresh_token) &&
|
|
75
|
+
secret.client_id &&
|
|
76
|
+
secret.client_secret))) {
|
|
77
|
+
errors.push("the secrets file is missing either property access_token or properties refresh_token, " +
|
|
78
|
+
"client_id and client_secret");
|
|
79
|
+
}
|
|
80
|
+
if (errors.length > 0) {
|
|
81
|
+
return { consistent: false, errors };
|
|
82
|
+
}
|
|
83
|
+
return { consistent: true };
|
|
26
84
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.create = void 0;
|
|
4
|
+
const constants_1 = require("../../../constants");
|
|
5
|
+
const handler_1 = require("../../handler");
|
|
6
|
+
const file_1 = require("../../handler/authentication/oauth/secretsProvider/file");
|
|
7
|
+
const logger_1 = require("../../../logger");
|
|
8
|
+
/* jscpd:ignore-end */
|
|
9
|
+
const create = () => async () => async () => {
|
|
10
|
+
const { output } = (0, logger_1.get)("commands.secrets.check");
|
|
11
|
+
await (0, file_1.readSecretsFile)();
|
|
12
|
+
output("the secrets file is consistent");
|
|
13
|
+
};
|
|
14
|
+
exports.create = create;
|
|
15
|
+
const refreshCommand = {
|
|
16
|
+
type: "command",
|
|
17
|
+
command: "check",
|
|
18
|
+
description: "check secrets file consistency",
|
|
19
|
+
handler: (0, handler_1.createNextHandler)("commands.config.secrets.check", (0, handler_1.createParseArgumentsHandler)(), (0, handler_1.createOptionsHandler)([
|
|
20
|
+
{ ...constants_1.OPTION_SECRETS_FILE, required: true, hidden: false },
|
|
21
|
+
]), (0, exports.create)()),
|
|
22
|
+
};
|
|
23
|
+
exports.default = refreshCommand;
|
|
@@ -6,10 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const refresh_command_1 = __importDefault(require("./refresh.command"));
|
|
7
7
|
const reset_command_1 = __importDefault(require("./reset.command"));
|
|
8
8
|
const show_command_1 = __importDefault(require("./show.command"));
|
|
9
|
+
const check_command_1 = __importDefault(require("./check.command"));
|
|
9
10
|
const secretsCommand = {
|
|
10
11
|
type: "topCommand",
|
|
11
12
|
command: "secrets",
|
|
12
13
|
description: "work with the locally stored secrets",
|
|
13
|
-
subCommands: [show_command_1.default, refresh_command_1.default, reset_command_1.default],
|
|
14
|
+
subCommands: [show_command_1.default, refresh_command_1.default, reset_command_1.default, check_command_1.default],
|
|
14
15
|
};
|
|
15
16
|
exports.default = secretsCommand;
|
|
@@ -16,6 +16,6 @@ const core_1 = require("../../../config/core");
|
|
|
16
16
|
const succeed_1 = require("../succeed");
|
|
17
17
|
exports.create = process.env.SUPPORT === "true"
|
|
18
18
|
? technicalJWT_1.create
|
|
19
|
-
: () => (0, next_1.create)("commands.handler.authentication", (0, resilient_1.create)((0, next_1.create)("commands.handler.authentication$oauth", (0, cache_1.create)(), (0, refreshToken_1.create)())), (0, options_1.create)(constants_1.OPTION_CLIENT_ID), (0, options_1.create)(constants_1.OPTION_CLIENT_SECRET), (0, options_1.create)(constants_1.OPTION_ACCESS_TOKEN), (0, options_1.create)(constants_1.OPTION_REFRESH_TOKEN), (0, options_1.create)(constants_1.OPTION_CODE), (0, options_1.create)(constants_1.OPTION_TOKEN_URL), (0, options_1.create)(constants_1.OPTION_AUTHORIZATION_URL), (0, core_1.getAuthenticationMethods)().includes(constants_1.AuthenticationMethod.passcode)
|
|
19
|
+
: () => (0, next_1.create)("commands.handler.authentication", (0, resilient_1.create)((0, next_1.create)("commands.handler.authentication$oauth", (0, cache_1.create)(), (0, refreshToken_1.create)())), (0, options_1.create)(constants_1.OPTION_CLIENT_ID), (0, options_1.create)(constants_1.OPTION_CLIENT_SECRET), (0, options_1.create)(constants_1.OPTION_ACCESS_TOKEN), (0, options_1.create)(constants_1.OPTION_REFRESH_TOKEN), (0, options_1.create)(constants_1.OPTION_CODE), (0, options_1.create)(constants_1.OPTION_TOKEN_URL), (0, options_1.create)(constants_1.OPTION_AUTHORIZATION_URL), (0, options_1.create)(constants_1.OPTION_AUTHORIZATION_FLOW), (0, core_1.getAuthenticationMethods)().includes(constants_1.AuthenticationMethod.passcode)
|
|
20
20
|
? (0, options_1.create)(constants_1.OPTION_PASSCODE)
|
|
21
21
|
: (0, succeed_1.create)(), (0, options_1.create)(constants_1.OPTION_SECRETS_FILE), (0, or_1.create)("commands.handler.authentication$handler", (0, setAuthorization_1.create)(), (0, passcode_1.create)(), (0, oauth_1.create)()));
|
|
@@ -1,25 +1,45 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.create = void 0;
|
|
4
|
+
exports.readSecretsFile = readSecretsFile;
|
|
4
5
|
const fs_extra_1 = require("fs-extra");
|
|
5
6
|
const next_1 = require("../../../next");
|
|
6
7
|
const constants_1 = require("../../../../../constants");
|
|
7
8
|
const logger_1 = require("../../../../../logger");
|
|
9
|
+
const config_1 = require("../../../../../config");
|
|
8
10
|
const options_1 = require("../../../../../utils/options");
|
|
9
11
|
const options_2 = require("../../../options");
|
|
10
12
|
const SecretsStorageSingleton_1 = require("../../../../../cache/secrets/SecretsStorageSingleton");
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
const utils_1 = require("../../../../../cache/secrets/utils");
|
|
14
|
+
async function readSecretsFile() {
|
|
15
|
+
const { output, error, debug } = (0, logger_1.get)("commands.handler.authentication.oauth.secretsProvider.file.readSecretsFile");
|
|
16
|
+
const secretsFile = (0, options_1.getOptionValueFromConfig)(constants_1.OPTION_SECRETS_FILE);
|
|
17
|
+
const config = (0, config_1.get)();
|
|
14
18
|
try {
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
let content = JSON.parse(await (0, fs_extra_1.readFile)(secretsFile, "utf8"));
|
|
20
|
+
content.tenantUrl = config.tenantUrl ?? content.tenantUrl;
|
|
21
|
+
content.customClient = (0, utils_1.isCustomClient)(content.client_id);
|
|
22
|
+
content = await (0, utils_1.updateUrls)(content);
|
|
17
23
|
debug("secrets found from file");
|
|
24
|
+
const consistent = (0, utils_1.isSecretConsistent)(content);
|
|
25
|
+
if (!consistent.consistent) {
|
|
26
|
+
output(`the provided secrets file is not consistent. ${consistent.errors.join(". ")}`);
|
|
27
|
+
throw new Error("inconsistent secrets file");
|
|
28
|
+
}
|
|
29
|
+
return content;
|
|
18
30
|
}
|
|
19
31
|
catch (err) {
|
|
20
32
|
error("failed to read secrets from file", err);
|
|
33
|
+
output(`The secrets file at location ${secretsFile} could not be read and JSON.parse'd.` +
|
|
34
|
+
`Does the file exist and is it a valid JSON file?`);
|
|
21
35
|
throw err;
|
|
22
36
|
}
|
|
37
|
+
}
|
|
38
|
+
const handler = async () => async () => {
|
|
39
|
+
const { info } = (0, logger_1.get)("commands.handler.authentication.oauth.secretsProvider.file");
|
|
40
|
+
info("reading secrets from file");
|
|
41
|
+
const content = await readSecretsFile();
|
|
42
|
+
await SecretsStorageSingleton_1.SecretsStorageSingleton.SINGLETON.storeSecret(content);
|
|
23
43
|
};
|
|
24
44
|
const create = () => (0, next_1.create)("commands.handler.authentication.oauth.secretsProvider.file", (0, options_2.create)([constants_1.OPTION_SECRETS_FILE]), handler);
|
|
25
45
|
exports.create = create;
|
|
@@ -20,6 +20,7 @@ const setOAuthFromOptions = async () => async () => {
|
|
|
20
20
|
token_url: (0, options_2.getOptionValueFromConfigGracefully)(constants_1.OPTION_TOKEN_URL),
|
|
21
21
|
access_token: (0, options_2.getOptionValueFromConfigGracefully)(constants_1.OPTION_ACCESS_TOKEN),
|
|
22
22
|
refresh_token: (0, options_2.getOptionValueFromConfigGracefully)(constants_1.OPTION_REFRESH_TOKEN),
|
|
23
|
+
authorization_flow: (0, options_2.getOptionValueFromConfigGracefully)(constants_1.OPTION_AUTHORIZATION_FLOW),
|
|
23
24
|
expires_in: expiresIn ? parseInt(expiresIn, 10) : undefined,
|
|
24
25
|
});
|
|
25
26
|
};
|
|
@@ -32,5 +33,6 @@ const create = () => (0, next_1.create)("handler.authentication.oauth.secretsPro
|
|
|
32
33
|
constants_1.OPTION_TOKEN_URL,
|
|
33
34
|
constants_1.OPTION_REFRESH_TOKEN,
|
|
34
35
|
constants_1.OPTION_EXPIRES_IN,
|
|
36
|
+
constants_1.OPTION_AUTHORIZATION_FLOW,
|
|
35
37
|
]))), setOAuthFromOptions);
|
|
36
38
|
exports.create = create;
|
|
@@ -5,6 +5,7 @@ const next_1 = require("../../../next");
|
|
|
5
5
|
const options_1 = require("../../../options");
|
|
6
6
|
const constants_1 = require("../../../../../constants");
|
|
7
7
|
const logger_1 = require("../../../../../logger");
|
|
8
|
+
const types_1 = require("../../../../../types");
|
|
8
9
|
const utils_1 = require("../utils");
|
|
9
10
|
const utils_2 = require("./utils");
|
|
10
11
|
const SecretsStorageSingleton_1 = require("../../../../../cache/secrets/SecretsStorageSingleton");
|
|
@@ -15,12 +16,17 @@ const handler = async () => async () => {
|
|
|
15
16
|
const secrets = await SecretsStorageSingleton_1.SecretsStorageSingleton.SINGLETON.getDefaultSecret();
|
|
16
17
|
if (!secrets.access_token && !secrets.refresh_token) {
|
|
17
18
|
debug("access token not available, retrieving token from server");
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
code,
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
if (secrets.authorization_flow === types_1.GrantType.authorization_code) {
|
|
20
|
+
const code = await (0, utils_2.getCode)(secrets.authorization_url, secrets.client_id);
|
|
21
|
+
debug("code received, reading token");
|
|
22
|
+
await (0, utils_1.readToken)({ code, grant_type: secrets.authorization_flow });
|
|
23
|
+
}
|
|
24
|
+
else if (secrets.authorization_flow === types_1.GrantType.client_credentials) {
|
|
25
|
+
await (0, utils_1.readToken)({ grant_type: secrets.authorization_flow });
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
throw new Error(`invalid grant type ${secrets.authorization_flow}`);
|
|
29
|
+
}
|
|
24
30
|
}
|
|
25
31
|
else if (secrets.access_token) {
|
|
26
32
|
debug("token available");
|
|
@@ -11,21 +11,29 @@ const options_1 = require("../../../../../utils/options");
|
|
|
11
11
|
const SecretsStorageSingleton_1 = require("../../../../../cache/secrets/SecretsStorageSingleton");
|
|
12
12
|
const openUtils_1 = require("../../../../../utils/openUtils");
|
|
13
13
|
const utils_2 = require("../../../../../logger/utils");
|
|
14
|
+
const types_1 = require("../../../../../types");
|
|
14
15
|
const getLogger = () => (0, logger_1.get)("commands.handler.authentication.oauth.tokenProvider.utils.refreshToken");
|
|
15
16
|
const refreshToken = async (forceRefresh = false) => {
|
|
16
17
|
const logger = getLogger();
|
|
17
18
|
const secrets = await SecretsStorageSingleton_1.SecretsStorageSingleton.SINGLETON.getDefaultSecret();
|
|
18
|
-
if ((!forceRefresh && !secrets.expires_after) ||
|
|
19
|
+
if ((!forceRefresh && !secrets.expires_after) ||
|
|
20
|
+
(secrets.authorization_flow === types_1.GrantType.authorization_code &&
|
|
21
|
+
!secrets.refresh_token)) {
|
|
19
22
|
(0, utils_2.logVerbose)(logger, "Access token cannot be refreshed. Is the refresh token available?");
|
|
20
23
|
throw new Error("invalid secrets information");
|
|
21
24
|
}
|
|
22
25
|
logger.info("checking token expiry date");
|
|
23
26
|
if (forceRefresh || (0, utils_1.isExpired)(secrets.expires_after)) {
|
|
24
27
|
logger.debug("access token is expired, refreshing token");
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
if (secrets.authorization_flow === types_1.GrantType.client_credentials) {
|
|
29
|
+
await (0, utils_1.readToken)({ grant_type: secrets.authorization_flow });
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
await (0, utils_1.readToken)({
|
|
33
|
+
refresh_token: secrets.refresh_token,
|
|
34
|
+
grant_type: types_1.GrantType.refresh_token,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
29
37
|
await (0, setAuthorization_1.updateAuthorization)();
|
|
30
38
|
}
|
|
31
39
|
else {
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { GrantType } from "../../../../types";
|
|
1
2
|
type Data = {
|
|
2
|
-
grant_type:
|
|
3
|
+
grant_type: GrantType.refresh_token;
|
|
3
4
|
refresh_token: string;
|
|
4
5
|
} | {
|
|
5
|
-
grant_type:
|
|
6
|
+
grant_type: GrantType.authorization_code;
|
|
6
7
|
code: string;
|
|
8
|
+
} | {
|
|
9
|
+
grant_type: GrantType.client_credentials;
|
|
7
10
|
};
|
|
8
11
|
export declare const calculateExpiresAfter: (expires_in: number) => number;
|
|
9
12
|
export declare const isExpired: (expires_after: number) => boolean;
|
|
@@ -45,6 +45,7 @@ const readToken = async (data) => {
|
|
|
45
45
|
})).data;
|
|
46
46
|
logger.debug(`token received: ${JSON.stringify(info, null, 2)}`);
|
|
47
47
|
await SecretsStorageSingleton_1.SecretsStorageSingleton.SINGLETON.storeSecret({
|
|
48
|
+
...secrets,
|
|
48
49
|
...info,
|
|
49
50
|
expires_after: (0, exports.calculateExpiresAfter)(info.expires_in),
|
|
50
51
|
});
|
|
@@ -11,6 +11,7 @@ const config_1 = require("../../../../config");
|
|
|
11
11
|
const logger_1 = require("../../../../logger");
|
|
12
12
|
const http_1 = require("../../../../utils/http");
|
|
13
13
|
const types_1 = require("./types");
|
|
14
|
+
const types_2 = require("../../../../types");
|
|
14
15
|
const getLogger = () => (0, logger_1.get)("commands.handler.authentication.technicalJWT.utils");
|
|
15
16
|
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/extensions
|
|
16
17
|
const cf = require("./cf.js");
|
|
@@ -56,7 +57,7 @@ const getTechnicalJwt = async () => {
|
|
|
56
57
|
url: `${secret.uaaUrl}/oauth/token`,
|
|
57
58
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
58
59
|
data: new url_1.URLSearchParams({
|
|
59
|
-
grant_type:
|
|
60
|
+
grant_type: types_2.GrantType.client_credentials,
|
|
60
61
|
response_type: "token",
|
|
61
62
|
client_id: secret.clientid,
|
|
62
63
|
client_secret: secret.clientsecret,
|
|
@@ -18,7 +18,7 @@ const REDIRECT_WITHOUT_CHANGE = [307, 308];
|
|
|
18
18
|
const fetchData = async (method, path, parameterMappings, responsePostProcessor) => {
|
|
19
19
|
const config = (0, config_1.get)();
|
|
20
20
|
(0, utils_1.checkConfiguration)(config);
|
|
21
|
-
const conf = (0, utils_1.buildHttpConfig)(method, path, parameterMappings);
|
|
21
|
+
const conf = await (0, utils_1.buildHttpConfig)(method, path, parameterMappings);
|
|
22
22
|
const response = await (0, http_1.fetch)(conf);
|
|
23
23
|
removeCsrfTokenFromConfig();
|
|
24
24
|
await (0, utils_1.handleResponse)(response.data, response.headers);
|
|
@@ -23,4 +23,4 @@ export declare const handleResponseData: (data: any, outputPath?: string) => Pro
|
|
|
23
23
|
export declare function handleResponseHeaders(headers: RawAxiosResponseHeaders | AxiosResponseHeaders): void;
|
|
24
24
|
export declare const handleResponse: (data: any, headers?: RawAxiosResponseHeaders | AxiosResponseHeaders) => Promise<void>;
|
|
25
25
|
export declare const configRequiresBody: (method: HTTPMethod) => boolean;
|
|
26
|
-
export declare const buildHttpConfig: (method: HTTPMethod, path: string, parameterMappings?: ParameterMappings) => HTTPConfig
|
|
26
|
+
export declare const buildHttpConfig: (method: HTTPMethod, path: string, parameterMappings?: ParameterMappings) => Promise<HTTPConfig>;
|
|
@@ -211,7 +211,7 @@ const handleResponse = async (data, headers) => {
|
|
|
211
211
|
exports.handleResponse = handleResponse;
|
|
212
212
|
const configRequiresBody = (method) => ["POST", "PUT", "PATCH"].includes(method.toUpperCase());
|
|
213
213
|
exports.configRequiresBody = configRequiresBody;
|
|
214
|
-
const buildHttpConfig = (method, path, parameterMappings) => {
|
|
214
|
+
const buildHttpConfig = async (method, path, parameterMappings) => {
|
|
215
215
|
const config = (0, config_1.get)();
|
|
216
216
|
const { url, body, headers } = (0, exports.buildParameters)(path, parameterMappings);
|
|
217
217
|
const httpConfig = {
|
|
@@ -31,7 +31,12 @@ const create = () => async (command) => {
|
|
|
31
31
|
}
|
|
32
32
|
logger.debug("reading request body from data");
|
|
33
33
|
try {
|
|
34
|
-
(0, config_1.set)({
|
|
34
|
+
(0, config_1.set)({
|
|
35
|
+
data: {
|
|
36
|
+
type: "json",
|
|
37
|
+
content: JSON.parse(input),
|
|
38
|
+
},
|
|
39
|
+
});
|
|
35
40
|
}
|
|
36
41
|
catch (err) {
|
|
37
42
|
(0, utils_1.logVerbose)(logger, "Failed to parse input data. Is it a valid escaped JSON string?");
|
|
@@ -58,6 +58,7 @@ const loginCommand = {
|
|
|
58
58
|
choices: utils_4.getBrowserChoices,
|
|
59
59
|
default: openUtils_1.getDefaultBrowser,
|
|
60
60
|
},
|
|
61
|
+
{ ...constants_1.OPTION_AUTHORIZATION_FLOW, hidden: false },
|
|
61
62
|
]), (0, handler_1.createMandatoryOptionsHandler)(), exports.verifyHost, (0, handler_1.createOauthHandler)(), initializeCache),
|
|
62
63
|
};
|
|
63
64
|
exports.default = loginCommand;
|
package/constants.d.ts
CHANGED
package/constants.js
CHANGED
|
@@ -3,8 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.OPTION_BROWSER = exports.OPTION_INPUT = exports.OPTION_FILE_PATH = exports.OPTION_OPTIONS_FILE = exports.CONFIG_PASSCODE_FUNCTION = exports.OPTION_PASSCODE = exports.OPTION_CODE = exports.OPTION_SECRETS_FILE = exports.OPTION_EXPIRES_IN = exports.OPTION_REFRESH_TOKEN = exports.OPTION_ACCESS_TOKEN = exports.OPTION_TOKEN_URL = exports.OPTION_AUTHORIZATION_URL = exports.OPTION_CLIENT_SECRET = exports.OPTION_CLIENT_ID = exports.OPTION_FORCE = exports.OPTION_VERBOSE = exports.OPTION_NO_PRETTY = exports.OPTION_OUTPUT = exports.OPTION_LOGIN_ID = exports.OPTION_HOST = exports.OPTION_HELP = exports.OPTION_VERSION = exports.CACHE_SECRETS_FILE = exports.PATH_TO_ERROR_HTML = exports.PATH_TO_SUCCESS_HTML = exports.X_OUTPUT_FILE_NAME = exports.X_DSP_API_DEPRECATED_PROPERTIES = exports.X_CSRF_TOKEN = exports.DISCOVERY_METADATA_PATH = exports.SEGMENTS_TO_REMOVE_FOR_PASSCODE_AUTH = exports.CLI_GENERIC_OPTIONS_HELP = exports.CLI_SUPPORTED_AUTHENTICATION_METHODS = exports.CLI_DEPRECATION_MESSAGE = exports.CLI_DEPRECATED = exports.CLI_VERSION = exports.CLI_SAP_HELP = exports.CLI_DISCOVERY_PATHS = exports.CLI_DESCRIPTION = exports.CLI_PACKAGE_NAME = exports.CLI_NAME = exports.ROOT_COMMAND = exports.AuthenticationMethod = exports.DISCOVERY_DOCUMENT_PREFIX = exports.VERSION = void 0;
|
|
6
|
+
exports.OPTION_AUTHORIZATION_FLOW = exports.OPTION_BROWSER = exports.OPTION_INPUT = exports.OPTION_FILE_PATH = exports.OPTION_OPTIONS_FILE = exports.CONFIG_PASSCODE_FUNCTION = exports.OPTION_PASSCODE = exports.OPTION_CODE = exports.OPTION_SECRETS_FILE = exports.OPTION_EXPIRES_IN = exports.OPTION_REFRESH_TOKEN = exports.OPTION_ACCESS_TOKEN = exports.OPTION_TOKEN_URL = exports.OPTION_AUTHORIZATION_URL = exports.OPTION_CLIENT_SECRET = exports.OPTION_CLIENT_ID = exports.OPTION_FORCE = exports.OPTION_VERBOSE = exports.OPTION_NO_PRETTY = exports.OPTION_OUTPUT = exports.OPTION_LOGIN_ID = exports.OPTION_HOST = exports.OPTION_HELP = exports.OPTION_VERSION = exports.CACHE_SECRETS_FILE = exports.PATH_TO_ERROR_HTML = exports.PATH_TO_SUCCESS_HTML = exports.X_OUTPUT_FILE_NAME = exports.X_DSP_API_DEPRECATED_PROPERTIES = exports.X_CSRF_TOKEN = exports.DISCOVERY_METADATA_PATH = exports.SEGMENTS_TO_REMOVE_FOR_PASSCODE_AUTH = exports.CLI_GENERIC_OPTIONS_HELP = exports.CLI_SUPPORTED_AUTHENTICATION_METHODS = exports.CLI_DEPRECATION_MESSAGE = exports.CLI_DEPRECATED = exports.CLI_VERSION = exports.CLI_SAP_HELP = exports.CLI_DISCOVERY_PATHS = exports.CLI_DESCRIPTION = exports.CLI_PACKAGE_NAME = exports.CLI_NAME = exports.ROOT_COMMAND = exports.AuthenticationMethod = exports.DISCOVERY_DOCUMENT_PREFIX = exports.VERSION = void 0;
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const types_1 = require("./types");
|
|
8
9
|
const utils_1 = require("./utils/utils");
|
|
9
10
|
exports.VERSION = (0, utils_1.getVersion)();
|
|
10
11
|
exports.DISCOVERY_DOCUMENT_PREFIX = "discovery-";
|
|
@@ -214,3 +215,15 @@ exports.OPTION_BROWSER = {
|
|
|
214
215
|
type: "select",
|
|
215
216
|
},
|
|
216
217
|
};
|
|
218
|
+
exports.OPTION_AUTHORIZATION_FLOW = {
|
|
219
|
+
longName: "authorization-flow",
|
|
220
|
+
description: "specifies the authorization flow to use",
|
|
221
|
+
args: [{ name: "authorization-flow" }],
|
|
222
|
+
choices: [
|
|
223
|
+
types_1.GrantType.authorization_code,
|
|
224
|
+
types_1.GrantType.client_credentials,
|
|
225
|
+
],
|
|
226
|
+
default: types_1.GrantType.authorization_code,
|
|
227
|
+
required: true,
|
|
228
|
+
hidden: true,
|
|
229
|
+
};
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cli-core",
|
|
3
|
-
"version": "2024.
|
|
3
|
+
"version": "2024.24.0",
|
|
4
4
|
"description": "Command-Line Interface (CLI) Core Module",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "SAP SE",
|
|
7
7
|
"homepage": "https://www.sap.com",
|
|
8
8
|
"main": "index.js",
|
|
9
9
|
"engines": {
|
|
10
|
-
"node": "^18 || ^19 || ^20 || ^21",
|
|
10
|
+
"node": "^18 || ^19 || ^20 || ^21 || ^22",
|
|
11
11
|
"npm": "^9 || ^10"
|
|
12
12
|
},
|
|
13
13
|
"keywords": [
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"compare-versions": "6.1.1",
|
|
24
24
|
"config": "3.3.12",
|
|
25
25
|
"dotenv": "16.4.5",
|
|
26
|
-
"form-data": "4.0.
|
|
26
|
+
"form-data": "4.0.1",
|
|
27
27
|
"fs-extra": "11.2.0",
|
|
28
28
|
"https": "1.0.0",
|
|
29
29
|
"https-proxy-agent": "7.0.5",
|
package/types.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export type Secret = {
|
|
|
16
16
|
jti: string;
|
|
17
17
|
scope: string;
|
|
18
18
|
id_token: string;
|
|
19
|
+
authorization_flow: GrantType;
|
|
19
20
|
};
|
|
20
21
|
export declare const ALLOWED_SECRET_TYPES: string[];
|
|
21
22
|
export { HTTPMethod, HTTPConfig, HTTPResponse };
|
|
@@ -211,3 +212,8 @@ export type DeprecatedProperties = Array<{
|
|
|
211
212
|
export type Command = TopCommand | LeafCommand;
|
|
212
213
|
export type AddCommands = (program: CommandType) => Promise<void>;
|
|
213
214
|
export type ResponsePostProcessor = (response: HTTPResponse) => Promise<void>;
|
|
215
|
+
export declare enum GrantType {
|
|
216
|
+
authorization_code = "authorization_code",
|
|
217
|
+
client_credentials = "client_credentials",
|
|
218
|
+
refresh_token = "refresh_token"
|
|
219
|
+
}
|
package/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CHARACTERS = exports.LogLevel = exports.ALLOWED_SECRET_TYPES = void 0;
|
|
3
|
+
exports.GrantType = exports.CHARACTERS = exports.LogLevel = exports.ALLOWED_SECRET_TYPES = void 0;
|
|
4
4
|
// unfortunately there does not seem to be a good solution to automatically
|
|
5
5
|
// create the below array from a statically defined type. Thus, the array must
|
|
6
6
|
// always be kept in sync with the type Secret defined above.
|
|
@@ -20,6 +20,7 @@ exports.ALLOWED_SECRET_TYPES = [
|
|
|
20
20
|
"jti",
|
|
21
21
|
"scope",
|
|
22
22
|
"id_token",
|
|
23
|
+
"authorization_flow",
|
|
23
24
|
];
|
|
24
25
|
var LogLevel;
|
|
25
26
|
(function (LogLevel) {
|
|
@@ -85,3 +86,9 @@ exports.CHARACTERS = [
|
|
|
85
86
|
"Y",
|
|
86
87
|
"Z",
|
|
87
88
|
];
|
|
89
|
+
var GrantType;
|
|
90
|
+
(function (GrantType) {
|
|
91
|
+
GrantType["authorization_code"] = "authorization_code";
|
|
92
|
+
GrantType["client_credentials"] = "client_credentials";
|
|
93
|
+
GrantType["refresh_token"] = "refresh_token";
|
|
94
|
+
})(GrantType || (exports.GrantType = GrantType = {}));
|
package/utils/utils.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare const getEngines: (cwd?: string) => PackageJson["engines"];
|
|
|
15
15
|
export declare const getBin: (cwd?: string) => string;
|
|
16
16
|
export declare const removeScopeFromPackageName: (packageName: string) => string;
|
|
17
17
|
export declare const parseTenant: (tenant: string) => string;
|
|
18
|
+
export declare function isValidURL(url: string): boolean;
|
|
18
19
|
export declare const getInfoFromTenant: (tenant: string, verbose: boolean, printOutput?: boolean) => {
|
|
19
20
|
host: string;
|
|
20
21
|
publicfqdn: string;
|
package/utils/utils.js
CHANGED
|
@@ -28,6 +28,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.toConstantCase = exports.sha256 = exports.getInfoFromTenant = exports.parseTenant = exports.removeScopeFromPackageName = exports.getBin = exports.getEngines = exports.getDescription = exports.getPackageName = exports.getName = exports.getVersion = exports.readPackageJson = exports.requireFile = exports.parseVersion = void 0;
|
|
30
30
|
exports.buildOptionName = buildOptionName;
|
|
31
|
+
exports.isValidURL = isValidURL;
|
|
31
32
|
exports.removeQueryParametersFromUrl = removeQueryParametersFromUrl;
|
|
32
33
|
const crypto = __importStar(require("crypto"));
|
|
33
34
|
const path_1 = __importDefault(require("path"));
|
|
@@ -124,6 +125,19 @@ const parseTenant = (tenant) => {
|
|
|
124
125
|
return new URL(t).host;
|
|
125
126
|
};
|
|
126
127
|
exports.parseTenant = parseTenant;
|
|
128
|
+
function isValidURL(url) {
|
|
129
|
+
const { debug, error } = getLogger();
|
|
130
|
+
try {
|
|
131
|
+
// eslint-disable-next-line no-new
|
|
132
|
+
new URL(url);
|
|
133
|
+
debug(`url ${url} is valid`);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
error(`url ${url} is no valid url`, err);
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
127
141
|
const getInfoFromTenant = (tenant, verbose, printOutput = true) => {
|
|
128
142
|
let protocol;
|
|
129
143
|
try {
|