@salesforce/core 4.0.0-v3.0 → 4.0.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/LICENSE.txt +1 -1
- package/README.md +93 -44
- package/lib/config/aliasesConfig.d.ts +12 -0
- package/lib/config/aliasesConfig.js +28 -0
- package/lib/config/authInfoConfig.d.ts +19 -0
- package/lib/config/authInfoConfig.js +35 -0
- package/lib/config/config.d.ts +181 -48
- package/lib/config/config.js +382 -159
- package/lib/config/configAggregator.d.ts +59 -42
- package/lib/config/configAggregator.js +135 -82
- package/lib/config/configFile.d.ts +2 -2
- package/lib/config/configFile.js +40 -31
- package/lib/config/configGroup.d.ts +9 -9
- package/lib/config/configGroup.js +13 -11
- package/lib/config/configStore.d.ts +9 -9
- package/lib/config/configStore.js +29 -26
- package/lib/config/envVars.d.ts +102 -0
- package/lib/config/envVars.js +457 -0
- package/lib/config/orgUsersConfig.d.ts +8 -0
- package/lib/config/orgUsersConfig.js +12 -0
- package/lib/config/sandboxOrgConfig.d.ts +8 -0
- package/lib/config/sandboxOrgConfig.js +12 -0
- package/lib/config/sandboxProcessCache.d.ts +16 -0
- package/lib/config/sandboxProcessCache.js +38 -0
- package/lib/config/tokensConfig.d.ts +10 -0
- package/lib/config/tokensConfig.js +29 -0
- package/lib/config/ttlConfig.d.ts +34 -0
- package/lib/config/ttlConfig.js +50 -0
- package/lib/crypto/crypto.js +29 -16
- package/lib/crypto/keyChain.js +4 -4
- package/lib/crypto/keyChainImpl.d.ts +5 -3
- package/lib/crypto/keyChainImpl.js +65 -66
- package/lib/crypto/secureBuffer.d.ts +1 -1
- package/lib/crypto/secureBuffer.js +1 -1
- package/lib/deviceOauthService.d.ts +5 -5
- package/lib/deviceOauthService.js +37 -33
- package/lib/exported.d.ts +22 -15
- package/lib/exported.js +49 -25
- package/lib/global.d.ts +17 -3
- package/lib/global.js +46 -13
- package/lib/lifecycleEvents.d.ts +39 -2
- package/lib/lifecycleEvents.js +77 -3
- package/lib/logger.d.ts +21 -11
- package/lib/logger.js +121 -105
- package/lib/messages.d.ts +53 -36
- package/lib/messages.js +89 -97
- package/lib/org/authInfo.d.ts +84 -72
- package/lib/org/authInfo.js +326 -320
- package/lib/org/authRemover.d.ts +16 -23
- package/lib/org/authRemover.js +62 -60
- package/lib/org/connection.d.ts +33 -59
- package/lib/org/connection.js +129 -190
- package/lib/org/index.js +6 -2
- package/lib/org/org.d.ts +263 -38
- package/lib/org/org.js +734 -149
- package/lib/org/orgConfigProperties.d.ts +69 -0
- package/lib/org/orgConfigProperties.js +121 -0
- package/lib/org/permissionSetAssignment.js +6 -15
- package/lib/org/scratchOrgCache.d.ts +20 -0
- package/lib/org/scratchOrgCache.js +33 -0
- package/lib/org/scratchOrgCreate.d.ts +54 -0
- package/lib/org/scratchOrgCreate.js +214 -0
- package/lib/org/scratchOrgErrorCodes.d.ts +10 -0
- package/lib/org/scratchOrgErrorCodes.js +79 -0
- package/lib/org/scratchOrgFeatureDeprecation.d.ts +26 -0
- package/lib/org/scratchOrgFeatureDeprecation.js +105 -0
- package/lib/org/scratchOrgInfoApi.d.ts +68 -0
- package/lib/org/scratchOrgInfoApi.js +416 -0
- package/lib/org/scratchOrgInfoGenerator.d.ts +64 -0
- package/lib/org/scratchOrgInfoGenerator.js +237 -0
- package/lib/org/scratchOrgLifecycleEvents.d.ts +10 -0
- package/lib/org/scratchOrgLifecycleEvents.js +41 -0
- package/lib/org/scratchOrgSettingsGenerator.d.ts +79 -0
- package/lib/org/scratchOrgSettingsGenerator.js +277 -0
- package/lib/org/scratchOrgTypes.d.ts +43 -0
- package/lib/{status/client.js → org/scratchOrgTypes.js} +1 -1
- package/lib/org/user.d.ts +7 -2
- package/lib/org/user.js +78 -54
- package/lib/schema/printer.d.ts +6 -0
- package/lib/schema/printer.js +49 -46
- package/lib/schema/validator.d.ts +12 -10
- package/lib/schema/validator.js +56 -76
- package/lib/{sfdxError.d.ts → sfError.d.ts} +12 -15
- package/lib/{sfdxError.js → sfError.js} +42 -24
- package/lib/{sfdxProject.d.ts → sfProject.d.ts} +75 -35
- package/lib/sfProject.js +651 -0
- package/lib/stateAggregator/accessors/aliasAccessor.d.ts +98 -0
- package/lib/stateAggregator/accessors/aliasAccessor.js +146 -0
- package/lib/stateAggregator/accessors/orgAccessor.d.ts +101 -0
- package/lib/stateAggregator/accessors/orgAccessor.js +240 -0
- package/lib/stateAggregator/accessors/sandboxAccessor.d.ts +8 -0
- package/lib/stateAggregator/accessors/sandboxAccessor.js +28 -0
- package/lib/stateAggregator/accessors/tokenAccessor.d.ts +63 -0
- package/lib/stateAggregator/accessors/tokenAccessor.js +80 -0
- package/lib/stateAggregator/index.d.ts +4 -0
- package/lib/stateAggregator/index.js +27 -0
- package/lib/stateAggregator/stateAggregator.d.ts +25 -0
- package/lib/stateAggregator/stateAggregator.js +46 -0
- package/lib/status/myDomainResolver.d.ts +1 -1
- package/lib/status/myDomainResolver.js +10 -10
- package/lib/status/pollingClient.d.ts +2 -6
- package/lib/status/pollingClient.js +38 -64
- package/lib/status/streamingClient.d.ts +5 -80
- package/lib/status/streamingClient.js +74 -94
- package/lib/status/types.d.ts +89 -0
- package/lib/status/types.js +18 -0
- package/lib/testSetup.d.ts +212 -79
- package/lib/testSetup.js +478 -182
- package/lib/util/cache.d.ts +11 -0
- package/lib/util/cache.js +70 -0
- package/lib/util/checkLightningDomain.d.ts +1 -0
- package/lib/util/checkLightningDomain.js +29 -0
- package/lib/util/directoryWriter.d.ts +12 -0
- package/lib/util/directoryWriter.js +54 -0
- package/lib/util/getJwtAudienceUrl.d.ts +4 -0
- package/lib/util/getJwtAudienceUrl.js +19 -0
- package/lib/util/internal.d.ts +28 -2
- package/lib/util/internal.js +65 -8
- package/lib/util/jsonXmlTools.d.ts +14 -0
- package/lib/util/jsonXmlTools.js +39 -0
- package/lib/util/mapKeys.d.ts +14 -0
- package/lib/util/mapKeys.js +52 -0
- package/lib/util/sfdc.d.ts +51 -63
- package/lib/util/sfdc.js +75 -127
- package/lib/util/sfdcUrl.d.ts +64 -0
- package/lib/util/sfdcUrl.js +197 -0
- package/lib/util/structuredWriter.d.ts +9 -0
- package/lib/util/structuredWriter.js +3 -0
- package/lib/util/zipWriter.d.ts +16 -0
- package/lib/util/zipWriter.js +68 -0
- package/lib/webOAuthServer.d.ts +20 -7
- package/lib/webOAuthServer.js +107 -60
- package/messageTransformer/messageTransformer.ts +93 -0
- package/messages/auth.md +11 -3
- package/messages/config.md +94 -6
- package/messages/connection.md +8 -0
- package/messages/core.json +3 -3
- package/messages/core.md +11 -1
- package/messages/envVars.md +313 -0
- package/messages/org.md +64 -0
- package/messages/scratchOrgCreate.md +23 -0
- package/messages/scratchOrgErrorCodes.md +115 -0
- package/messages/scratchOrgFeatureDeprecation.md +11 -0
- package/messages/scratchOrgInfoApi.md +20 -0
- package/messages/scratchOrgInfoGenerator.md +27 -0
- package/messages/user.md +12 -0
- package/package.json +138 -66
- package/CHANGELOG.md +0 -699
- package/lib/config/aliases.d.ts +0 -56
- package/lib/config/aliases.js +0 -96
- package/lib/config/globalInfoConfig.d.ts +0 -74
- package/lib/config/globalInfoConfig.js +0 -144
- package/lib/config/keychainConfig.d.ts +0 -19
- package/lib/config/keychainConfig.js +0 -43
- package/lib/config/sfdxDataHandler.d.ts +0 -36
- package/lib/config/sfdxDataHandler.js +0 -165
- package/lib/sfdxProject.js +0 -546
- package/lib/status/client.d.ts +0 -15
- package/lib/util/fs.d.ts +0 -198
- package/lib/util/fs.js +0 -374
package/lib/org/authInfo.js
CHANGED
|
@@ -5,160 +5,30 @@
|
|
|
5
5
|
* Licensed under the BSD 3-Clause license.
|
|
6
6
|
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
7
7
|
*/
|
|
8
|
+
/* eslint-disable class-methods-use-this */
|
|
8
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.AuthInfo = exports.DEFAULT_CONNECTED_APP_INFO =
|
|
10
|
+
exports.AuthInfo = exports.DEFAULT_CONNECTED_APP_INFO = void 0;
|
|
10
11
|
const crypto_1 = require("crypto");
|
|
11
|
-
const url_1 = require("url");
|
|
12
|
-
const dns = require("dns");
|
|
13
12
|
const path_1 = require("path");
|
|
14
|
-
const url_2 = require("url");
|
|
15
13
|
const os = require("os");
|
|
14
|
+
const fs = require("fs");
|
|
16
15
|
const kit_1 = require("@salesforce/kit");
|
|
17
16
|
const ts_types_1 = require("@salesforce/ts-types");
|
|
18
17
|
const jsforce_1 = require("jsforce");
|
|
19
|
-
|
|
20
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
21
|
-
// @ts-ignore
|
|
22
|
-
const Transport = require("jsforce/lib/transport");
|
|
18
|
+
const transport_1 = require("jsforce/lib/transport");
|
|
23
19
|
const jwt = require("jsonwebtoken");
|
|
24
|
-
const aliases_1 = require("../config/aliases");
|
|
25
20
|
const config_1 = require("../config/config");
|
|
26
21
|
const configAggregator_1 = require("../config/configAggregator");
|
|
27
22
|
const logger_1 = require("../logger");
|
|
28
|
-
const
|
|
29
|
-
const fs_1 = require("../util/fs");
|
|
23
|
+
const sfError_1 = require("../sfError");
|
|
30
24
|
const sfdc_1 = require("../util/sfdc");
|
|
31
|
-
const
|
|
32
|
-
const globalInfoConfig_1 = require("../config/globalInfoConfig");
|
|
25
|
+
const stateAggregator_1 = require("../stateAggregator");
|
|
33
26
|
const messages_1 = require("../messages");
|
|
27
|
+
const sfdcUrl_1 = require("../util/sfdcUrl");
|
|
34
28
|
const connection_1 = require("./connection");
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
'authInfoOverwriteError',
|
|
39
|
-
'namedOrgNotFound',
|
|
40
|
-
'orgDataNotAvailableError',
|
|
41
|
-
'orgDataNotAvailableError.actions',
|
|
42
|
-
'refreshTokenAuthError',
|
|
43
|
-
'jwtAuthError',
|
|
44
|
-
'authCodeUsernameRetrievalError',
|
|
45
|
-
'authCodeExchangeError',
|
|
46
|
-
]);
|
|
47
|
-
// Extend OAuth2 to add JWT Bearer Token Flow support.
|
|
48
|
-
class JwtOAuth2 extends jsforce_1.OAuth2 {
|
|
49
|
-
constructor(options) {
|
|
50
|
-
super(options);
|
|
51
|
-
}
|
|
52
|
-
jwtAuthorize(innerToken, callback) {
|
|
53
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
54
|
-
// @ts-ignore
|
|
55
|
-
return super._postParams({
|
|
56
|
-
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
|
57
|
-
assertion: innerToken,
|
|
58
|
-
}, callback);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Extend OAuth2 to add code verifier support for the auth code (web auth) flow
|
|
63
|
-
* const oauth2 = new OAuth2WithVerifier({ loginUrl, clientSecret, clientId, redirectUri });
|
|
64
|
-
*
|
|
65
|
-
* const authUrl = oauth2.getAuthorizationUrl({
|
|
66
|
-
* state: 'foo',
|
|
67
|
-
* prompt: 'login',
|
|
68
|
-
* scope: 'api web'
|
|
69
|
-
* });
|
|
70
|
-
* console.log(authUrl);
|
|
71
|
-
* const authCode = await retrieveCode();
|
|
72
|
-
* const authInfo = await AuthInfo.create({ oauth2Options: { clientId, clientSecret, loginUrl, authCode }, oauth2});
|
|
73
|
-
* console.log(`access token: ${authInfo.getFields(true).accessToken}`);
|
|
74
|
-
*/
|
|
75
|
-
class OAuth2WithVerifier extends jsforce_1.OAuth2 {
|
|
76
|
-
constructor(options) {
|
|
77
|
-
super(options);
|
|
78
|
-
// Set a code verifier string for OAuth authorization
|
|
79
|
-
this.codeVerifier = base64UrlEscape(crypto_1.randomBytes(Math.ceil(128)).toString('base64'));
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Overrides jsforce.OAuth2.getAuthorizationUrl. Get Salesforce OAuth2 authorization page
|
|
83
|
-
* URL to redirect user agent, adding a verification code for added security.
|
|
84
|
-
*
|
|
85
|
-
* @param params
|
|
86
|
-
*/
|
|
87
|
-
getAuthorizationUrl(params) {
|
|
88
|
-
// code verifier must be a base 64 url encoded hash of 128 bytes of random data. Our random data is also
|
|
89
|
-
// base 64 url encoded. See Connection.create();
|
|
90
|
-
const codeChallenge = base64UrlEscape(crypto_1.createHash('sha256').update(this.codeVerifier).digest('base64'));
|
|
91
|
-
kit_1.set(params, 'code_challenge', codeChallenge);
|
|
92
|
-
return super.getAuthorizationUrl(params);
|
|
93
|
-
}
|
|
94
|
-
async requestToken(code, callback) {
|
|
95
|
-
return super.requestToken(code, callback);
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Overrides jsforce.OAuth2._postParams because jsforce's oauth impl doesn't support
|
|
99
|
-
* coder_verifier and code_challenge. This enables the server to disallow trading a one-time auth code
|
|
100
|
-
* for an access/refresh token when the verifier and challenge are out of alignment.
|
|
101
|
-
*
|
|
102
|
-
* See https://github.com/jsforce/jsforce/issues/665
|
|
103
|
-
*/
|
|
104
|
-
async _postParams(params, callback) {
|
|
105
|
-
kit_1.set(params, 'code_verifier', this.codeVerifier);
|
|
106
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
107
|
-
// @ts-ignore TODO: need better typings for jsforce
|
|
108
|
-
return super._postParams(params, callback);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
exports.OAuth2WithVerifier = OAuth2WithVerifier;
|
|
112
|
-
/**
|
|
113
|
-
* Salesforce URLs.
|
|
114
|
-
*/
|
|
115
|
-
var SfdcUrl;
|
|
116
|
-
(function (SfdcUrl) {
|
|
117
|
-
SfdcUrl["SANDBOX"] = "https://test.salesforce.com";
|
|
118
|
-
SfdcUrl["PRODUCTION"] = "https://login.salesforce.com";
|
|
119
|
-
})(SfdcUrl = exports.SfdcUrl || (exports.SfdcUrl = {}));
|
|
120
|
-
function isSandboxUrl(options) {
|
|
121
|
-
var _a;
|
|
122
|
-
const createdOrgInstance = ts_types_1.getString(options, 'createdOrgInstance', '').trim().toLowerCase();
|
|
123
|
-
const loginUrl = (_a = options.loginUrl) !== null && _a !== void 0 ? _a : '';
|
|
124
|
-
return (/^cs|s$/gi.test(createdOrgInstance) ||
|
|
125
|
-
/sandbox\.my\.salesforce\.com/gi.test(loginUrl) || // enhanced domains >= 230
|
|
126
|
-
/(cs[0-9]+(\.my|)\.salesforce\.com)/gi.test(loginUrl) || // my domains on CS instance OR CS instance without my domain
|
|
127
|
-
/([a-z]{3}[0-9]+s\.sfdc-.+\.salesforce\.com)/gi.test(loginUrl) || // falcon sandbox ex: usa2s.sfdc-whatever.salesforce.com
|
|
128
|
-
/([a-z]{3}[0-9]+s\.sfdc-.+\.force\.com)/gi.test(loginUrl) || // falcon sandbox ex: usa2s.sfdc-whatever.salesforce.com
|
|
129
|
-
url_2.parse(loginUrl).hostname === 'test.salesforce.com');
|
|
130
|
-
}
|
|
131
|
-
async function resolvesToSandbox(options) {
|
|
132
|
-
if (isSandboxUrl(options)) {
|
|
133
|
-
return true;
|
|
134
|
-
}
|
|
135
|
-
let cnames = [];
|
|
136
|
-
if (options.loginUrl) {
|
|
137
|
-
const myDomainResolver = await myDomainResolver_1.MyDomainResolver.create({ url: new url_1.URL(options.loginUrl) });
|
|
138
|
-
cnames = await myDomainResolver.getCnames();
|
|
139
|
-
}
|
|
140
|
-
return cnames.some((cname) => isSandboxUrl(Object.assign(Object.assign({}, options), { loginUrl: cname })));
|
|
141
|
-
}
|
|
142
|
-
async function getJwtAudienceUrl(options) {
|
|
143
|
-
var _a;
|
|
144
|
-
// environment variable is used as an override
|
|
145
|
-
if (process.env.SFDX_AUDIENCE_URL) {
|
|
146
|
-
return process.env.SFDX_AUDIENCE_URL;
|
|
147
|
-
}
|
|
148
|
-
if (options.loginUrl && sfdc_1.sfdc.isInternalUrl(options.loginUrl)) {
|
|
149
|
-
// This is for internal developers when just doing authorize;
|
|
150
|
-
return options.loginUrl;
|
|
151
|
-
}
|
|
152
|
-
if (await resolvesToSandbox(options)) {
|
|
153
|
-
return SfdcUrl.SANDBOX;
|
|
154
|
-
}
|
|
155
|
-
const createdOrgInstance = ts_types_1.getString(options, 'createdOrgInstance', '').trim().toLowerCase();
|
|
156
|
-
if (/^gs1/gi.test(createdOrgInstance) || /(gs1.my.salesforce.com)/gi.test((_a = options.loginUrl) !== null && _a !== void 0 ? _a : '')) {
|
|
157
|
-
return 'https://gs1.salesforce.com';
|
|
158
|
-
}
|
|
159
|
-
return SfdcUrl.PRODUCTION;
|
|
160
|
-
}
|
|
161
|
-
exports.getJwtAudienceUrl = getJwtAudienceUrl;
|
|
29
|
+
const orgConfigProperties_1 = require("./orgConfigProperties");
|
|
30
|
+
const org_1 = require("./org");
|
|
31
|
+
const messages = new messages_1.Messages('@salesforce/core', 'core', new Map([["authInfoCreationError", "Must pass a username and/or OAuth options when creating an AuthInfo instance."], ["authInfoOverwriteError", "Cannot create an AuthInfo instance that will overwrite existing auth data."], ["authInfoOverwriteError.actions", ["Create the AuthInfo instance using existing auth data by just passing the username. E.g., `AuthInfo.create({ username: 'my@user.org' });`."]], ["authCodeExchangeError", "Error authenticating with auth code due to: %s"], ["authCodeUsernameRetrievalError", "Could not retrieve the username after successful auth code exchange.\n\nDue to: %s"], ["jwtAuthError", "Error authenticating with JWT config due to: %s"], ["jwtAuthErrors", "Error authenticating with JWT.\nErrors encountered:\n%s"], ["refreshTokenAuthError", "Error authenticating with the refresh token due to: %s"], ["orgDataNotAvailableError", "An attempt to refresh the authentication token failed with a 'Data Not Found Error'. The org identified by username %s does not appear to exist. Likely cause is that the org was deleted by another user or has expired."], ["orgDataNotAvailableError.actions", ["Run `sfdx force:org:list --clean` to remove stale org authentications.", "Use `sfdx force:config:set` to update the defaultusername.", "Use `sfdx force:org:create` to create a new org.", "Use `sfdx auth` to authenticate an existing org."]], ["namedOrgNotFound", "No authorization information found for %s."], ["noAliasesFound", "Nothing to set."], ["invalidFormat", "Setting aliases must be in the format <key>=<value> but found: [%s]."], ["invalidJsonCasing", "All JSON input must have heads down camelcase keys. E.g., `{ sfdcLoginUrl: \"https://login.salesforce.com\" }`\nFound \"%s\" at %s"], ["missingClientId", "Client ID is required for JWT authentication."]]));
|
|
162
32
|
// parses the id field returned from jsForce oauth2 methods to get
|
|
163
33
|
// user ID and org ID.
|
|
164
34
|
function parseIdUrl(idUrl) {
|
|
@@ -173,23 +43,8 @@ function parseIdUrl(idUrl) {
|
|
|
173
43
|
}
|
|
174
44
|
exports.DEFAULT_CONNECTED_APP_INFO = {
|
|
175
45
|
clientId: 'PlatformCLI',
|
|
176
|
-
|
|
177
|
-
// creates new AuthInfos. Currently that is the auth:* commands which
|
|
178
|
-
// aren't owned by this core library. These values need to be here
|
|
179
|
-
// for any old auth files where the id and secret aren't stored.
|
|
180
|
-
//
|
|
181
|
-
// Ideally, this would be removed at some point in the distant future
|
|
182
|
-
// when all auth files now have the clientId stored in it.
|
|
183
|
-
legacyClientId: 'SalesforceDevelopmentExperience',
|
|
184
|
-
legacyClientSecret: '1384510088588713504',
|
|
46
|
+
clientSecret: '',
|
|
185
47
|
};
|
|
186
|
-
// Makes a nodejs base64 encoded string compatible with rfc4648 alternative encoding for urls.
|
|
187
|
-
// @param base64Encoded a nodejs base64 encoded string
|
|
188
|
-
function base64UrlEscape(base64Encoded) {
|
|
189
|
-
// builtin node js base 64 encoding is not 64 url compatible.
|
|
190
|
-
// See https://toolsn.ietf.org/html/rfc4648#section-5
|
|
191
|
-
return base64Encoded.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
192
|
-
}
|
|
193
48
|
/**
|
|
194
49
|
* Handles persistence and fetching of user authentication information using
|
|
195
50
|
* JWT, OAuth, or refresh tokens. Sets up the refresh flows that jsForce will
|
|
@@ -235,7 +90,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
235
90
|
super(options);
|
|
236
91
|
// Possibly overridden in create
|
|
237
92
|
this.usingAccessToken = false;
|
|
238
|
-
this.options = options
|
|
93
|
+
this.options = options ?? {};
|
|
239
94
|
}
|
|
240
95
|
/**
|
|
241
96
|
* Returns the default instance url
|
|
@@ -243,63 +98,83 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
243
98
|
* @returns {string}
|
|
244
99
|
*/
|
|
245
100
|
static getDefaultInstanceUrl() {
|
|
246
|
-
const configuredInstanceUrl = configAggregator_1.ConfigAggregator.getValue(
|
|
247
|
-
return configuredInstanceUrl
|
|
101
|
+
const configuredInstanceUrl = configAggregator_1.ConfigAggregator.getValue(orgConfigProperties_1.OrgConfigProperties.ORG_INSTANCE_URL)?.value;
|
|
102
|
+
return configuredInstanceUrl ?? sfdcUrl_1.SfdcUrl.PRODUCTION;
|
|
248
103
|
}
|
|
249
104
|
/**
|
|
250
105
|
* Get a list of all authorizations based on auth files stored in the global directory.
|
|
106
|
+
* One can supply a filter (see @param orgAuthFilter) and calling this function without
|
|
107
|
+
* a filter will return all authorizations.
|
|
251
108
|
*
|
|
252
|
-
* @returns
|
|
109
|
+
* @param orgAuthFilter A predicate function that returns true for those org authorizations that are to be retained.
|
|
110
|
+
*
|
|
111
|
+
* @returns {Promise<OrgAuthorization[]>}
|
|
253
112
|
*/
|
|
254
|
-
static async listAllAuthorizations() {
|
|
255
|
-
const
|
|
256
|
-
const
|
|
257
|
-
const
|
|
113
|
+
static async listAllAuthorizations(orgAuthFilter = (orgAuth) => !!orgAuth) {
|
|
114
|
+
const stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
|
|
115
|
+
const config = (await configAggregator_1.ConfigAggregator.create()).getConfigInfo();
|
|
116
|
+
const orgs = await stateAggregator.orgs.readAll();
|
|
258
117
|
const final = [];
|
|
259
|
-
for (const
|
|
260
|
-
const username = ts_types_1.ensureString(
|
|
261
|
-
const
|
|
118
|
+
for (const org of orgs) {
|
|
119
|
+
const username = (0, ts_types_1.ensureString)(org.username);
|
|
120
|
+
const aliases = stateAggregator.aliases.getAll(username) ?? undefined;
|
|
121
|
+
// Get a list of configuration values that are set to either the username or one
|
|
122
|
+
// of the aliases
|
|
123
|
+
const configs = config
|
|
124
|
+
.filter((c) => aliases.includes(c.value) || c.value === username)
|
|
125
|
+
.map((c) => c.key);
|
|
262
126
|
try {
|
|
127
|
+
// prevent ConfigFile collision bug
|
|
128
|
+
// eslint-disable-next-line no-await-in-loop
|
|
263
129
|
const authInfo = await AuthInfo.create({ username });
|
|
264
|
-
const { orgId, instanceUrl } = authInfo.getFields();
|
|
130
|
+
const { orgId, instanceUrl, devHubUsername, expirationDate, isDevHub } = authInfo.getFields();
|
|
265
131
|
final.push({
|
|
266
|
-
|
|
132
|
+
aliases,
|
|
133
|
+
configs,
|
|
267
134
|
username,
|
|
268
|
-
orgId,
|
|
269
135
|
instanceUrl,
|
|
136
|
+
isScratchOrg: Boolean(devHubUsername),
|
|
137
|
+
isDevHub: isDevHub ?? false,
|
|
138
|
+
// eslint-disable-next-line no-await-in-loop
|
|
139
|
+
isSandbox: await stateAggregator.sandboxes.hasFile(orgId),
|
|
140
|
+
orgId: orgId,
|
|
270
141
|
accessToken: authInfo.getConnectionOptions().accessToken,
|
|
271
142
|
oauthMethod: authInfo.isJwt() ? 'jwt' : authInfo.isOauth() ? 'web' : 'token',
|
|
272
|
-
|
|
143
|
+
isExpired: Boolean(devHubUsername) && expirationDate
|
|
144
|
+
? new Date((0, ts_types_1.ensureString)(expirationDate)).getTime() < new Date().getTime()
|
|
145
|
+
: 'unknown',
|
|
273
146
|
});
|
|
274
147
|
}
|
|
275
148
|
catch (err) {
|
|
276
149
|
final.push({
|
|
277
|
-
|
|
150
|
+
aliases,
|
|
151
|
+
configs,
|
|
278
152
|
username,
|
|
279
|
-
orgId:
|
|
280
|
-
instanceUrl:
|
|
153
|
+
orgId: org.orgId,
|
|
154
|
+
instanceUrl: org.instanceUrl,
|
|
281
155
|
accessToken: undefined,
|
|
282
156
|
oauthMethod: 'unknown',
|
|
283
157
|
error: err.message,
|
|
284
|
-
|
|
158
|
+
isExpired: 'unknown',
|
|
285
159
|
});
|
|
286
160
|
}
|
|
287
161
|
}
|
|
288
|
-
return final;
|
|
162
|
+
return final.filter(orgAuthFilter);
|
|
289
163
|
}
|
|
290
164
|
/**
|
|
291
165
|
* Returns true if one or more authentications are persisted.
|
|
292
166
|
*/
|
|
293
167
|
static async hasAuthentications() {
|
|
294
168
|
try {
|
|
295
|
-
const auths = (await
|
|
296
|
-
return !kit_1.isEmpty(auths);
|
|
169
|
+
const auths = await (await stateAggregator_1.StateAggregator.getInstance()).orgs.list();
|
|
170
|
+
return !(0, kit_1.isEmpty)(auths);
|
|
297
171
|
}
|
|
298
172
|
catch (err) {
|
|
299
|
-
|
|
173
|
+
const error = err;
|
|
174
|
+
if (error.name === 'OrgDataNotAvailableError' || error.code === 'ENOENT') {
|
|
300
175
|
return false;
|
|
301
176
|
}
|
|
302
|
-
throw
|
|
177
|
+
throw error;
|
|
303
178
|
}
|
|
304
179
|
}
|
|
305
180
|
/**
|
|
@@ -308,14 +183,16 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
308
183
|
* @param options The options to generate the URL.
|
|
309
184
|
*/
|
|
310
185
|
static getAuthorizationUrl(options, oauth2) {
|
|
311
|
-
|
|
186
|
+
// Always use a verifier for enhanced security
|
|
187
|
+
options.useVerifier = true;
|
|
188
|
+
const oauth2Verifier = oauth2 ?? new jsforce_1.OAuth2(options);
|
|
312
189
|
// The state parameter allows the redirectUri callback listener to ignore request
|
|
313
190
|
// that don't contain the state value.
|
|
314
191
|
const params = {
|
|
315
|
-
state: crypto_1.randomBytes(Math.ceil(6)).toString('hex'),
|
|
192
|
+
state: (0, crypto_1.randomBytes)(Math.ceil(6)).toString('hex'),
|
|
316
193
|
prompt: 'login',
|
|
317
194
|
// Default connected app is 'refresh_token api web'
|
|
318
|
-
scope: options.scope
|
|
195
|
+
scope: options.scope ?? kit_1.env.getString('SFDX_AUTH_SCOPES', 'refresh_token api web'),
|
|
319
196
|
};
|
|
320
197
|
return oauth2Verifier.getAuthorizationUrl(params);
|
|
321
198
|
}
|
|
@@ -331,7 +208,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
331
208
|
static parseSfdxAuthUrl(sfdxAuthUrl) {
|
|
332
209
|
const match = sfdxAuthUrl.match(/^force:\/\/([a-zA-Z0-9._-]+):([a-zA-Z0-9._-]*):([a-zA-Z0-9._-]+={0,2})@([a-zA-Z0-9._-]+)/);
|
|
333
210
|
if (!match) {
|
|
334
|
-
throw new
|
|
211
|
+
throw new sfError_1.SfError('Invalid SFDX auth URL. Must be in the format "force://<clientId>:<clientSecret>:<refreshToken>@<instanceUrl>". Note that the SFDX auth URL uses the "force" protocol, and not "http" or "https". Also note that the "instanceUrl" inside the SFDX auth URL doesn\'t include the protocol ("https://").', 'INVALID_SFDX_AUTH_URL');
|
|
335
212
|
}
|
|
336
213
|
const [, clientId, clientSecret, refreshToken, loginUrl] = match;
|
|
337
214
|
return {
|
|
@@ -341,6 +218,64 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
341
218
|
loginUrl: `https://${loginUrl}`,
|
|
342
219
|
};
|
|
343
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* Given a set of decrypted fields and an authInfo, determine if the org belongs to an available
|
|
223
|
+
* dev hub.
|
|
224
|
+
*
|
|
225
|
+
* @param fields
|
|
226
|
+
* @param orgAuthInfo
|
|
227
|
+
*/
|
|
228
|
+
static async identifyPossibleScratchOrgs(fields, orgAuthInfo) {
|
|
229
|
+
// fields property is passed in because the consumers of this method have performed the decrypt.
|
|
230
|
+
// This is so we don't have to call authInfo.getFields(true) and decrypt again OR accidentally save an
|
|
231
|
+
// authInfo before it is necessary.
|
|
232
|
+
const logger = await logger_1.Logger.child('Common', { tag: 'identifyPossibleScratchOrgs' });
|
|
233
|
+
// return if we already know the hub org we know it is a devhub or prod-like or no orgId present
|
|
234
|
+
if (fields.isDevHub || fields.devHubUsername || !fields.orgId)
|
|
235
|
+
return;
|
|
236
|
+
logger.debug('getting devHubs');
|
|
237
|
+
// TODO: return if url is not sandbox-like to avoid constantly asking about production orgs
|
|
238
|
+
// TODO: someday we make this easier by asking the org if it is a scratch org
|
|
239
|
+
const hubAuthInfos = await AuthInfo.getDevHubAuthInfos();
|
|
240
|
+
logger.debug(`found ${hubAuthInfos.length} DevHubs`);
|
|
241
|
+
if (hubAuthInfos.length === 0)
|
|
242
|
+
return;
|
|
243
|
+
// ask all those orgs if they know this orgId
|
|
244
|
+
await Promise.all(hubAuthInfos.map(async (hubAuthInfo) => {
|
|
245
|
+
try {
|
|
246
|
+
const soi = await AuthInfo.queryScratchOrg(hubAuthInfo.username, fields.orgId);
|
|
247
|
+
// if any return a result
|
|
248
|
+
logger.debug(`found orgId ${fields.orgId} in devhub ${hubAuthInfo.username}`);
|
|
249
|
+
try {
|
|
250
|
+
await orgAuthInfo.save({
|
|
251
|
+
...fields,
|
|
252
|
+
devHubUsername: hubAuthInfo.username,
|
|
253
|
+
expirationDate: soi.ExpirationDate,
|
|
254
|
+
isScratch: true,
|
|
255
|
+
});
|
|
256
|
+
logger.debug(`set ${hubAuthInfo.username} as devhub and expirationDate ${soi.ExpirationDate} for scratch org ${orgAuthInfo.getUsername()}`);
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
logger.debug(`error updating auth file for ${orgAuthInfo.getUsername()}`, error);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
logger.error(`Error connecting to devhub ${hubAuthInfo.username}`, error);
|
|
264
|
+
}
|
|
265
|
+
}));
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Find all dev hubs available in the local environment.
|
|
269
|
+
*/
|
|
270
|
+
static async getDevHubAuthInfos() {
|
|
271
|
+
return AuthInfo.listAllAuthorizations((possibleHub) => possibleHub?.isDevHub ?? false);
|
|
272
|
+
}
|
|
273
|
+
static async queryScratchOrg(devHubUsername, scratchOrgId) {
|
|
274
|
+
const devHubOrg = await org_1.Org.create({ aliasOrUsername: devHubUsername });
|
|
275
|
+
const conn = devHubOrg.getConnection();
|
|
276
|
+
const data = await conn.singleRecordQuery(`select Id, ExpirationDate from ScratchOrgInfo where ScratchOrg = '${(0, sfdc_1.trimTo15)(scratchOrgId)}'`);
|
|
277
|
+
return data;
|
|
278
|
+
}
|
|
344
279
|
/**
|
|
345
280
|
* Get the username.
|
|
346
281
|
*/
|
|
@@ -381,12 +316,12 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
381
316
|
*/
|
|
382
317
|
async save(authData) {
|
|
383
318
|
this.update(authData);
|
|
384
|
-
const username = ts_types_1.ensure(this.getUsername());
|
|
385
|
-
if (sfdc_1.
|
|
319
|
+
const username = (0, ts_types_1.ensure)(this.getUsername());
|
|
320
|
+
if ((0, sfdc_1.matchesAccessToken)(username)) {
|
|
386
321
|
this.logger.debug('Username is an accesstoken. Skip saving authinfo to disk.');
|
|
387
322
|
return this;
|
|
388
323
|
}
|
|
389
|
-
await this.
|
|
324
|
+
await this.stateAggregator.orgs.write(username);
|
|
390
325
|
this.logger.info(`Saved auth info for username: ${username}`);
|
|
391
326
|
return this;
|
|
392
327
|
}
|
|
@@ -397,13 +332,10 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
397
332
|
* @param authData Authorization fields to update.
|
|
398
333
|
*/
|
|
399
334
|
update(authData) {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
this.
|
|
403
|
-
|
|
404
|
-
const mergedFields = Object.assign({}, existingFields || {}, authData);
|
|
405
|
-
this.globalInfo.setOrg(this.getUsername(), mergedFields);
|
|
406
|
-
this.logger.info(`Updated auth info for username: ${this.getUsername()}`);
|
|
335
|
+
if (authData && (0, ts_types_1.isPlainObject)(authData)) {
|
|
336
|
+
this.username = authData.username ?? this.username;
|
|
337
|
+
this.stateAggregator.orgs.update(this.username, authData);
|
|
338
|
+
this.logger.info(`Updated auth info for username: ${this.username}`);
|
|
407
339
|
}
|
|
408
340
|
return this;
|
|
409
341
|
}
|
|
@@ -436,9 +368,9 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
436
368
|
// Decrypt a user provided client secret or use the default.
|
|
437
369
|
opts = {
|
|
438
370
|
oauth2: {
|
|
439
|
-
loginUrl: instanceUrl
|
|
440
|
-
clientId:
|
|
441
|
-
redirectUri:
|
|
371
|
+
loginUrl: instanceUrl ?? sfdcUrl_1.SfdcUrl.PRODUCTION,
|
|
372
|
+
clientId: this.getClientId(),
|
|
373
|
+
redirectUri: this.getRedirectUri(),
|
|
442
374
|
},
|
|
443
375
|
accessToken,
|
|
444
376
|
instanceUrl,
|
|
@@ -448,21 +380,27 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
448
380
|
// decrypt the fields
|
|
449
381
|
return opts;
|
|
450
382
|
}
|
|
383
|
+
getClientId() {
|
|
384
|
+
return this.getFields()?.clientId ?? exports.DEFAULT_CONNECTED_APP_INFO.clientId;
|
|
385
|
+
}
|
|
386
|
+
getRedirectUri() {
|
|
387
|
+
return 'http://localhost:1717/OauthRedirect';
|
|
388
|
+
}
|
|
451
389
|
/**
|
|
452
390
|
* Get the authorization fields.
|
|
453
391
|
*
|
|
454
392
|
* @param decrypt Decrypt the fields.
|
|
455
393
|
*/
|
|
456
394
|
getFields(decrypt) {
|
|
457
|
-
return this.
|
|
395
|
+
return this.stateAggregator.orgs.get(this.username, decrypt) ?? {};
|
|
458
396
|
}
|
|
459
397
|
/**
|
|
460
398
|
* Get the org front door (used for web based oauth flows)
|
|
461
399
|
*/
|
|
462
400
|
getOrgFrontDoorUrl() {
|
|
463
401
|
const authFields = this.getFields(true);
|
|
464
|
-
const base = ts_types_1.ensureString(authFields.instanceUrl).replace(/\/+$/, '');
|
|
465
|
-
const accessToken = ts_types_1.ensureString(authFields.accessToken);
|
|
402
|
+
const base = (0, ts_types_1.ensureString)(authFields.instanceUrl).replace(/\/+$/, '');
|
|
403
|
+
const accessToken = (0, ts_types_1.ensureString)(authFields.accessToken);
|
|
466
404
|
return `${base}/secur/frontdoor.jsp?sid=${accessToken}`;
|
|
467
405
|
}
|
|
468
406
|
/**
|
|
@@ -478,38 +416,64 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
478
416
|
*/
|
|
479
417
|
getSfdxAuthUrl() {
|
|
480
418
|
const decryptedFields = this.getFields(true);
|
|
481
|
-
const instanceUrl = ts_types_1.ensure(decryptedFields.instanceUrl, 'undefined instanceUrl').replace(/^https?:\/\//, '');
|
|
419
|
+
const instanceUrl = (0, ts_types_1.ensure)(decryptedFields.instanceUrl, 'undefined instanceUrl').replace(/^https?:\/\//, '');
|
|
482
420
|
let sfdxAuthUrl = 'force://';
|
|
483
421
|
if (decryptedFields.clientId) {
|
|
484
|
-
sfdxAuthUrl += `${decryptedFields.clientId}:${decryptedFields.clientSecret
|
|
422
|
+
sfdxAuthUrl += `${decryptedFields.clientId}:${decryptedFields.clientSecret ?? ''}:`;
|
|
485
423
|
}
|
|
486
|
-
sfdxAuthUrl += `${ts_types_1.ensure(decryptedFields.refreshToken, 'undefined refreshToken')}@${instanceUrl}`;
|
|
424
|
+
sfdxAuthUrl += `${(0, ts_types_1.ensure)(decryptedFields.refreshToken, 'undefined refreshToken')}@${instanceUrl}`;
|
|
487
425
|
return sfdxAuthUrl;
|
|
488
426
|
}
|
|
489
427
|
/**
|
|
490
|
-
*
|
|
428
|
+
* Convenience function to handle typical side effects encountered when dealing with an AuthInfo.
|
|
429
|
+
* Given the values supplied in parameter sideEffects, this function will set auth alias, default auth
|
|
430
|
+
* and default dev hub.
|
|
431
|
+
*
|
|
432
|
+
* @param sideEffects - instance of AuthSideEffects
|
|
433
|
+
*/
|
|
434
|
+
async handleAliasAndDefaultSettings(sideEffects) {
|
|
435
|
+
if (sideEffects.alias ||
|
|
436
|
+
sideEffects.setDefault ||
|
|
437
|
+
sideEffects.setDefaultDevHub ||
|
|
438
|
+
typeof sideEffects.setTracksSource === 'boolean') {
|
|
439
|
+
if (sideEffects.alias)
|
|
440
|
+
await this.setAlias(sideEffects.alias);
|
|
441
|
+
if (sideEffects.setDefault)
|
|
442
|
+
await this.setAsDefault({ org: true });
|
|
443
|
+
if (sideEffects.setDefaultDevHub)
|
|
444
|
+
await this.setAsDefault({ devHub: true });
|
|
445
|
+
if (typeof sideEffects.setTracksSource === 'boolean') {
|
|
446
|
+
await this.save({ tracksSource: sideEffects.setTracksSource });
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
await this.save();
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Set the target-env (default) or the target-dev-hub to the alias if
|
|
491
455
|
* it exists otherwise to the username. Method will try to set the local
|
|
492
456
|
* config first but will default to global config if that fails.
|
|
493
457
|
*
|
|
494
458
|
* @param options
|
|
495
459
|
*/
|
|
496
|
-
async setAsDefault(options) {
|
|
460
|
+
async setAsDefault(options = { org: true }) {
|
|
497
461
|
let config;
|
|
498
462
|
// if we fail to create the local config, default to the global config
|
|
499
463
|
try {
|
|
500
464
|
config = await config_1.Config.create({ isGlobal: false });
|
|
501
465
|
}
|
|
502
|
-
catch
|
|
466
|
+
catch {
|
|
503
467
|
config = await config_1.Config.create({ isGlobal: true });
|
|
504
468
|
}
|
|
505
|
-
const username = ts_types_1.ensureString(this.getUsername());
|
|
506
|
-
const
|
|
507
|
-
const value =
|
|
508
|
-
if (options.
|
|
509
|
-
config.set(
|
|
469
|
+
const username = (0, ts_types_1.ensureString)(this.getUsername());
|
|
470
|
+
const alias = this.stateAggregator.aliases.get(username);
|
|
471
|
+
const value = alias ?? username;
|
|
472
|
+
if (options.org) {
|
|
473
|
+
config.set(orgConfigProperties_1.OrgConfigProperties.TARGET_ORG, value);
|
|
510
474
|
}
|
|
511
|
-
if (options.
|
|
512
|
-
config.set(
|
|
475
|
+
if (options.devHub) {
|
|
476
|
+
config.set(orgConfigProperties_1.OrgConfigProperties.TARGET_DEV_HUB, value);
|
|
513
477
|
}
|
|
514
478
|
await config.write();
|
|
515
479
|
}
|
|
@@ -519,17 +483,16 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
519
483
|
* @param alias alias to set
|
|
520
484
|
*/
|
|
521
485
|
async setAlias(alias) {
|
|
522
|
-
|
|
523
|
-
await
|
|
486
|
+
this.stateAggregator.aliases.set(alias, this.getUsername());
|
|
487
|
+
await this.stateAggregator.aliases.write();
|
|
524
488
|
}
|
|
525
489
|
/**
|
|
526
490
|
* Initializes an instance of the AuthInfo class.
|
|
527
491
|
*/
|
|
528
492
|
async init() {
|
|
529
|
-
|
|
530
|
-
this.globalInfo = await globalInfoConfig_1.GlobalInfo.getInstance();
|
|
493
|
+
this.stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
|
|
531
494
|
const username = this.options.username;
|
|
532
|
-
const authOptions = this.options.oauth2Options
|
|
495
|
+
const authOptions = this.options.oauth2Options ?? this.options.accessTokenOptions;
|
|
533
496
|
// Must specify either username and/or options
|
|
534
497
|
if (!username && !authOptions) {
|
|
535
498
|
throw messages.createError('authInfoCreationError');
|
|
@@ -537,21 +500,21 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
537
500
|
// If a username AND oauth options, ensure an authorization for the username doesn't
|
|
538
501
|
// already exist. Throw if it does so we don't overwrite the authorization.
|
|
539
502
|
if (username && authOptions) {
|
|
540
|
-
|
|
541
|
-
if (authExists) {
|
|
503
|
+
if (await this.stateAggregator.orgs.hasFile(username)) {
|
|
542
504
|
throw messages.createError('authInfoOverwriteError');
|
|
543
505
|
}
|
|
544
506
|
}
|
|
545
|
-
const oauthUsername = username
|
|
507
|
+
const oauthUsername = username ?? authOptions?.username;
|
|
546
508
|
if (oauthUsername) {
|
|
547
509
|
this.username = oauthUsername;
|
|
510
|
+
await this.stateAggregator.orgs.read(oauthUsername, false, false);
|
|
548
511
|
} // Else it will be set in initAuthOptions below.
|
|
549
512
|
// If the username is an access token, use that for auth and don't persist
|
|
550
|
-
if (ts_types_1.isString(oauthUsername) && sfdc_1.
|
|
513
|
+
if ((0, ts_types_1.isString)(oauthUsername) && (0, sfdc_1.matchesAccessToken)(oauthUsername)) {
|
|
551
514
|
// Need to initAuthOptions the logger and authInfoCrypto since we don't call init()
|
|
552
515
|
this.logger = await logger_1.Logger.child('AuthInfo');
|
|
553
516
|
const aggregator = await configAggregator_1.ConfigAggregator.create();
|
|
554
|
-
const instanceUrl = this.getInstanceUrl(
|
|
517
|
+
const instanceUrl = this.getInstanceUrl(aggregator, authOptions);
|
|
555
518
|
this.update({
|
|
556
519
|
accessToken: oauthUsername,
|
|
557
520
|
instanceUrl,
|
|
@@ -561,16 +524,16 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
561
524
|
this.usingAccessToken = true;
|
|
562
525
|
}
|
|
563
526
|
// If a username with NO oauth options, ensure authorization already exist.
|
|
564
|
-
else if (username && !authOptions && !this.
|
|
527
|
+
else if (username && !authOptions && !(await this.stateAggregator.orgs.exists(username))) {
|
|
565
528
|
throw messages.createError('namedOrgNotFound', [username]);
|
|
566
529
|
}
|
|
567
530
|
else {
|
|
568
531
|
await this.initAuthOptions(authOptions);
|
|
569
532
|
}
|
|
570
533
|
}
|
|
571
|
-
getInstanceUrl(
|
|
572
|
-
const instanceUrl =
|
|
573
|
-
return instanceUrl
|
|
534
|
+
getInstanceUrl(aggregator, options) {
|
|
535
|
+
const instanceUrl = options?.instanceUrl ?? aggregator.getPropertyValue(orgConfigProperties_1.OrgConfigProperties.ORG_INSTANCE_URL);
|
|
536
|
+
return instanceUrl ?? sfdcUrl_1.SfdcUrl.PRODUCTION;
|
|
574
537
|
}
|
|
575
538
|
/**
|
|
576
539
|
* Initialize this AuthInfo instance with the specified options. If options are not provided, initialize it from cache
|
|
@@ -578,7 +541,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
578
541
|
*
|
|
579
542
|
* @param options Options to be used for creating an OAuth2 instance.
|
|
580
543
|
*
|
|
581
|
-
* **Throws** *{@link
|
|
544
|
+
* **Throws** *{@link SfError}{ name: 'NamedOrgNotFoundError' }* Org information does not exist.
|
|
582
545
|
* @returns {Promise<AuthInfo>}
|
|
583
546
|
*/
|
|
584
547
|
async initAuthOptions(options) {
|
|
@@ -586,11 +549,11 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
586
549
|
// If options were passed, use those before checking cache and reading an auth file.
|
|
587
550
|
let authConfig;
|
|
588
551
|
if (options) {
|
|
589
|
-
options = kit_1.cloneJson(options);
|
|
552
|
+
options = (0, kit_1.cloneJson)(options);
|
|
590
553
|
if (this.isTokenOptions(options)) {
|
|
591
554
|
authConfig = options;
|
|
592
|
-
const userInfo = await this.retrieveUserInfo(ts_types_1.ensureString(options.instanceUrl), ts_types_1.ensureString(options.accessToken));
|
|
593
|
-
this.update({ username: userInfo
|
|
555
|
+
const userInfo = await this.retrieveUserInfo((0, ts_types_1.ensureString)(options.instanceUrl), (0, ts_types_1.ensureString)(options.accessToken));
|
|
556
|
+
this.update({ username: userInfo?.username, orgId: userInfo?.organizationId });
|
|
594
557
|
}
|
|
595
558
|
else {
|
|
596
559
|
if (this.options.parentUsername) {
|
|
@@ -603,48 +566,50 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
603
566
|
// Grab whatever flow is defined
|
|
604
567
|
Object.assign(options, {
|
|
605
568
|
clientSecret: parentFields.clientSecret,
|
|
606
|
-
privateKey: parentFields.privateKey ? path_1.resolve(parentFields.privateKey) : parentFields.privateKey,
|
|
569
|
+
privateKey: parentFields.privateKey ? (0, path_1.resolve)(parentFields.privateKey) : parentFields.privateKey,
|
|
607
570
|
});
|
|
608
571
|
}
|
|
609
572
|
}
|
|
610
573
|
// jwt flow
|
|
611
574
|
// Support both sfdx and jsforce private key values
|
|
612
575
|
if (!options.privateKey && options.privateKeyFile) {
|
|
613
|
-
options.privateKey = path_1.resolve(options.privateKeyFile);
|
|
576
|
+
options.privateKey = (0, path_1.resolve)(options.privateKeyFile);
|
|
614
577
|
}
|
|
615
578
|
if (options.privateKey) {
|
|
616
|
-
authConfig = await this.
|
|
579
|
+
authConfig = await this.authJwt(options);
|
|
617
580
|
}
|
|
618
581
|
else if (!options.authCode && options.refreshToken) {
|
|
619
582
|
// refresh token flow (from sfdxUrl or OAuth refreshFn)
|
|
620
583
|
authConfig = await this.buildRefreshTokenConfig(options);
|
|
621
584
|
}
|
|
585
|
+
else if (this.options.oauth2 instanceof jsforce_1.OAuth2) {
|
|
586
|
+
// authcode exchange / web auth flow
|
|
587
|
+
authConfig = await this.exchangeToken(options, this.options.oauth2);
|
|
588
|
+
}
|
|
622
589
|
else {
|
|
623
|
-
|
|
624
|
-
// authcode exchange / web auth flow
|
|
625
|
-
authConfig = await this.exchangeToken(options, this.options.oauth2);
|
|
626
|
-
}
|
|
627
|
-
else {
|
|
628
|
-
authConfig = await this.exchangeToken(options);
|
|
629
|
-
}
|
|
590
|
+
authConfig = await this.exchangeToken(options);
|
|
630
591
|
}
|
|
631
592
|
}
|
|
593
|
+
authConfig.isDevHub = await this.determineIfDevHub((0, ts_types_1.ensureString)(authConfig.instanceUrl), (0, ts_types_1.ensureString)(authConfig.accessToken));
|
|
594
|
+
if (authConfig.username)
|
|
595
|
+
await this.stateAggregator.orgs.read(authConfig.username, false, false);
|
|
632
596
|
// Update the auth fields WITH encryption
|
|
633
597
|
this.update(authConfig);
|
|
634
598
|
}
|
|
635
599
|
return this;
|
|
636
600
|
}
|
|
601
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
637
602
|
async loadDecryptedAuthFromConfig(username) {
|
|
638
603
|
// Fetch from the persisted auth file
|
|
639
|
-
const authInfo = this.
|
|
604
|
+
const authInfo = this.stateAggregator.orgs.get(username, true);
|
|
640
605
|
if (!authInfo) {
|
|
641
606
|
throw messages.createError('namedOrgNotFound', [username]);
|
|
642
607
|
}
|
|
643
608
|
return authInfo;
|
|
644
609
|
}
|
|
645
610
|
isTokenOptions(options) {
|
|
646
|
-
// Although
|
|
647
|
-
// which WILL have an access token as well, but it should be considered an
|
|
611
|
+
// Although OAuth2Config does not contain refreshToken, privateKey, or privateKeyFile, a JS consumer could still pass those in
|
|
612
|
+
// which WILL have an access token as well, but it should be considered an OAuth2Config at that point.
|
|
648
613
|
return ('accessToken' in options &&
|
|
649
614
|
!('refreshToken' in options) &&
|
|
650
615
|
!('privateKey' in options) &&
|
|
@@ -662,89 +627,119 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
662
627
|
return await callback(null, fields.accessToken);
|
|
663
628
|
}
|
|
664
629
|
catch (err) {
|
|
665
|
-
|
|
630
|
+
const error = err;
|
|
631
|
+
if (error?.message?.includes('Data Not Available')) {
|
|
666
632
|
// Set cause to keep original stacktrace
|
|
667
|
-
return await callback(messages.createError('orgDataNotAvailableError', [this.getUsername()], [],
|
|
633
|
+
return await callback(messages.createError('orgDataNotAvailableError', [this.getUsername()], [], error));
|
|
668
634
|
}
|
|
669
|
-
return await callback(
|
|
635
|
+
return await callback(error);
|
|
670
636
|
}
|
|
671
637
|
}
|
|
638
|
+
async readJwtKey(keyFile) {
|
|
639
|
+
return fs.promises.readFile(keyFile, 'utf8');
|
|
640
|
+
}
|
|
672
641
|
// Build OAuth config for a JWT auth flow
|
|
673
|
-
async
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
algorithm: 'RS256',
|
|
683
|
-
});
|
|
684
|
-
const oauth2 = new JwtOAuth2({ loginUrl: options.loginUrl });
|
|
642
|
+
async authJwt(options) {
|
|
643
|
+
if (!options.clientId) {
|
|
644
|
+
throw messages.createError('missingClientId');
|
|
645
|
+
}
|
|
646
|
+
const privateKeyContents = await this.readJwtKey((0, ts_types_1.ensureString)(options.privateKey));
|
|
647
|
+
const { loginUrl = sfdcUrl_1.SfdcUrl.PRODUCTION } = options;
|
|
648
|
+
const url = new sfdcUrl_1.SfdcUrl(loginUrl);
|
|
649
|
+
const createdOrgInstance = (this.getFields().createdOrgInstance ?? '').trim().toLowerCase();
|
|
650
|
+
const audienceUrl = await url.getJwtAudienceUrl(createdOrgInstance);
|
|
685
651
|
let authFieldsBuilder;
|
|
686
|
-
|
|
687
|
-
|
|
652
|
+
const authErrors = [];
|
|
653
|
+
// given that we can no longer depend on instance names or URls to determine audience, let's try them all
|
|
654
|
+
const loginAndAudienceUrls = (0, sfdcUrl_1.getLoginAudienceCombos)(audienceUrl, loginUrl);
|
|
655
|
+
for (const [login, audience] of loginAndAudienceUrls) {
|
|
656
|
+
try {
|
|
657
|
+
// sequentially, in probabilistic order
|
|
658
|
+
// eslint-disable-next-line no-await-in-loop
|
|
659
|
+
authFieldsBuilder = await this.tryJwtAuth(options.clientId, login, audience, privateKeyContents);
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
catch (err) {
|
|
663
|
+
const error = err;
|
|
664
|
+
const message = error.message.includes('audience')
|
|
665
|
+
? `${error.message} [audience=${audience} login=${login}]`
|
|
666
|
+
: error.message;
|
|
667
|
+
authErrors.push(message);
|
|
668
|
+
}
|
|
688
669
|
}
|
|
689
|
-
|
|
690
|
-
|
|
670
|
+
if (!authFieldsBuilder) {
|
|
671
|
+
// messages.createError expects names to end in `error` and this one says Errors so do it manually.
|
|
672
|
+
throw new sfError_1.SfError(messages.getMessage('jwtAuthErrors', [authErrors.join('\n')]), 'JwtAuthError');
|
|
691
673
|
}
|
|
692
674
|
const authFields = {
|
|
693
|
-
accessToken: ts_types_1.asString(authFieldsBuilder.access_token),
|
|
694
|
-
orgId: parseIdUrl(ts_types_1.ensureString(authFieldsBuilder.id)).orgId,
|
|
675
|
+
accessToken: (0, ts_types_1.asString)(authFieldsBuilder.access_token),
|
|
676
|
+
orgId: parseIdUrl((0, ts_types_1.ensureString)(authFieldsBuilder.id)).orgId,
|
|
695
677
|
loginUrl: options.loginUrl,
|
|
696
678
|
privateKey: options.privateKey,
|
|
697
679
|
clientId: options.clientId,
|
|
698
680
|
};
|
|
699
|
-
const instanceUrl = ts_types_1.ensureString(authFieldsBuilder.instance_url);
|
|
700
|
-
const
|
|
681
|
+
const instanceUrl = (0, ts_types_1.ensureString)(authFieldsBuilder.instance_url);
|
|
682
|
+
const sfdcUrl = new sfdcUrl_1.SfdcUrl(instanceUrl);
|
|
701
683
|
try {
|
|
702
684
|
// Check if the url is resolvable. This can fail when my-domains have not been replicated.
|
|
703
|
-
await
|
|
685
|
+
await sfdcUrl.lookup();
|
|
704
686
|
authFields.instanceUrl = instanceUrl;
|
|
705
687
|
}
|
|
706
688
|
catch (err) {
|
|
707
|
-
this.logger.debug(
|
|
689
|
+
this.logger.debug(
|
|
690
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
691
|
+
`Instance URL [${authFieldsBuilder.instance_url}] is not available. DNS lookup failed. Using loginUrl [${options.loginUrl}] instead. This may result in a "Destination URL not reset" error.`);
|
|
708
692
|
authFields.instanceUrl = options.loginUrl;
|
|
709
693
|
}
|
|
710
694
|
return authFields;
|
|
711
695
|
}
|
|
696
|
+
async tryJwtAuth(clientId, loginUrl, audienceUrl, privateKeyContents) {
|
|
697
|
+
const jwtToken = jwt.sign({
|
|
698
|
+
iss: clientId,
|
|
699
|
+
sub: this.getUsername(),
|
|
700
|
+
aud: audienceUrl,
|
|
701
|
+
exp: Date.now() + 300,
|
|
702
|
+
}, privateKeyContents, {
|
|
703
|
+
algorithm: 'RS256',
|
|
704
|
+
});
|
|
705
|
+
const oauth2 = new jsforce_1.JwtOAuth2({ loginUrl });
|
|
706
|
+
// jsforce has it types as any
|
|
707
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
708
|
+
return (0, ts_types_1.ensureJsonMap)(await oauth2.jwtAuthorize(jwtToken));
|
|
709
|
+
}
|
|
712
710
|
// Build OAuth config for a refresh token auth flow
|
|
713
711
|
async buildRefreshTokenConfig(options) {
|
|
714
712
|
// Ideally, this would be removed at some point in the distant future when all auth files
|
|
715
713
|
// now have the clientId stored in it.
|
|
716
714
|
if (!options.clientId) {
|
|
717
|
-
options.clientId = exports.DEFAULT_CONNECTED_APP_INFO.
|
|
718
|
-
options.clientSecret = exports.DEFAULT_CONNECTED_APP_INFO.
|
|
715
|
+
options.clientId = exports.DEFAULT_CONNECTED_APP_INFO.clientId;
|
|
716
|
+
options.clientSecret = exports.DEFAULT_CONNECTED_APP_INFO.clientSecret;
|
|
717
|
+
}
|
|
718
|
+
if (!options.redirectUri) {
|
|
719
|
+
options.redirectUri = this.getRedirectUri();
|
|
719
720
|
}
|
|
720
721
|
const oauth2 = new jsforce_1.OAuth2(options);
|
|
721
722
|
let authFieldsBuilder;
|
|
722
723
|
try {
|
|
723
|
-
authFieldsBuilder = await oauth2.refreshToken(ts_types_1.ensure(options.refreshToken));
|
|
724
|
+
authFieldsBuilder = await oauth2.refreshToken((0, ts_types_1.ensure)(options.refreshToken));
|
|
724
725
|
}
|
|
725
726
|
catch (err) {
|
|
726
727
|
throw messages.createError('refreshTokenAuthError', [err.message]);
|
|
727
728
|
}
|
|
728
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-
|
|
729
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
729
730
|
// @ts-ignore
|
|
730
731
|
const { orgId } = parseIdUrl(authFieldsBuilder.id);
|
|
731
732
|
let username = this.getUsername();
|
|
732
733
|
if (!username) {
|
|
733
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
734
|
-
// @ts-ignore
|
|
735
734
|
const userInfo = await this.retrieveUserInfo(authFieldsBuilder.instance_url, authFieldsBuilder.access_token);
|
|
736
|
-
username = ts_types_1.ensureString(userInfo
|
|
735
|
+
username = (0, ts_types_1.ensureString)(userInfo?.username);
|
|
737
736
|
}
|
|
738
737
|
return {
|
|
739
738
|
orgId,
|
|
740
739
|
username,
|
|
741
740
|
accessToken: authFieldsBuilder.access_token,
|
|
742
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
743
|
-
// @ts-ignore TODO: need better typings for jsforce
|
|
744
741
|
instanceUrl: authFieldsBuilder.instance_url,
|
|
745
|
-
|
|
746
|
-
// @ts-ignore TODO: need better typings for jsforce
|
|
747
|
-
loginUrl: options.loginUrl || authFieldsBuilder.instance_url,
|
|
742
|
+
loginUrl: options.loginUrl ?? authFieldsBuilder.instance_url,
|
|
748
743
|
refreshToken: options.refreshToken,
|
|
749
744
|
clientId: options.clientId,
|
|
750
745
|
clientSecret: options.clientSecret,
|
|
@@ -757,37 +752,37 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
757
752
|
* @param oauth2 The oauth2 extension that includes a code_challenge
|
|
758
753
|
*/
|
|
759
754
|
async exchangeToken(options, oauth2 = new jsforce_1.OAuth2(options)) {
|
|
755
|
+
if (!oauth2.redirectUri) {
|
|
756
|
+
oauth2.redirectUri = this.getRedirectUri();
|
|
757
|
+
}
|
|
758
|
+
if (!oauth2.clientId) {
|
|
759
|
+
oauth2.clientId = this.getClientId();
|
|
760
|
+
}
|
|
760
761
|
// Exchange the auth code for an access token and refresh token.
|
|
761
762
|
let authFields;
|
|
762
763
|
try {
|
|
763
764
|
this.logger.info(`Exchanging auth code for access token using loginUrl: ${options.loginUrl}`);
|
|
764
|
-
authFields = await oauth2.requestToken(ts_types_1.ensure(options.authCode));
|
|
765
|
+
authFields = await oauth2.requestToken((0, ts_types_1.ensure)(options.authCode));
|
|
765
766
|
}
|
|
766
767
|
catch (err) {
|
|
767
768
|
throw messages.createError('authCodeExchangeError', [err.message]);
|
|
768
769
|
}
|
|
769
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
770
|
-
// @ts-ignore TODO: need better typings for jsforce
|
|
771
770
|
const { orgId } = parseIdUrl(authFields.id);
|
|
772
771
|
let username = this.getUsername();
|
|
773
772
|
// Only need to query for the username if it isn't known. For example, a new auth code exchange
|
|
774
773
|
// rather than refreshing a token on an existing connection.
|
|
775
774
|
if (!username) {
|
|
776
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-
|
|
775
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
777
776
|
// @ts-ignore
|
|
778
777
|
const userInfo = await this.retrieveUserInfo(authFields.instance_url, authFields.access_token);
|
|
779
|
-
username = userInfo
|
|
778
|
+
username = userInfo?.username;
|
|
780
779
|
}
|
|
781
780
|
return {
|
|
782
781
|
accessToken: authFields.access_token,
|
|
783
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
784
|
-
// @ts-ignore TODO: need better typings for jsforce
|
|
785
782
|
instanceUrl: authFields.instance_url,
|
|
786
783
|
orgId,
|
|
787
784
|
username,
|
|
788
|
-
|
|
789
|
-
// @ts-ignore TODO: need better typings for jsforce
|
|
790
|
-
loginUrl: options.loginUrl || authFields.instance_url,
|
|
785
|
+
loginUrl: options.loginUrl ?? authFields.instance_url,
|
|
791
786
|
refreshToken: authFields.refresh_token,
|
|
792
787
|
clientId: options.clientId,
|
|
793
788
|
clientSecret: options.clientSecret,
|
|
@@ -798,26 +793,27 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
798
793
|
// but we don't want to create circular dependencies or lots of snowflakes
|
|
799
794
|
// within this file to support it.
|
|
800
795
|
const apiVersion = 'v51.0'; // hardcoding to v51.0 just for this call is okay.
|
|
801
|
-
const instance = ts_types_1.ensure(instanceUrl);
|
|
802
|
-
const baseUrl = new
|
|
803
|
-
const userInfoUrl = `${baseUrl}services/oauth2/userinfo`;
|
|
796
|
+
const instance = (0, ts_types_1.ensure)(instanceUrl);
|
|
797
|
+
const baseUrl = new sfdcUrl_1.SfdcUrl(instance);
|
|
798
|
+
const userInfoUrl = `${baseUrl.toString()}services/oauth2/userinfo`;
|
|
804
799
|
const headers = Object.assign({ Authorization: `Bearer ${accessToken}` }, connection_1.SFDX_HTTP_HEADERS);
|
|
805
800
|
try {
|
|
806
801
|
this.logger.info(`Sending request for Username after successful auth code exchange to URL: ${userInfoUrl}`);
|
|
807
|
-
let response = await new
|
|
802
|
+
let response = await new transport_1.default().httpRequest({ url: userInfoUrl, method: 'GET', headers });
|
|
808
803
|
if (response.statusCode >= 400) {
|
|
809
804
|
this.throwUserGetException(response);
|
|
810
805
|
}
|
|
811
806
|
else {
|
|
812
|
-
const userInfoJson = kit_1.parseJsonMap(response.body);
|
|
813
|
-
const url = `${baseUrl}
|
|
807
|
+
const userInfoJson = (0, kit_1.parseJsonMap)(response.body);
|
|
808
|
+
const url = `${baseUrl.toString()}services/data/${apiVersion}/sobjects/User/${userInfoJson.user_id}`;
|
|
814
809
|
this.logger.info(`Sending request for User SObject after successful auth code exchange to URL: ${url}`);
|
|
815
|
-
response = await new
|
|
810
|
+
response = await new transport_1.default().httpRequest({ url, method: 'GET', headers });
|
|
816
811
|
if (response.statusCode >= 400) {
|
|
817
812
|
this.throwUserGetException(response);
|
|
818
813
|
}
|
|
819
814
|
else {
|
|
820
|
-
|
|
815
|
+
// eslint-disable-next-line camelcase
|
|
816
|
+
userInfoJson.preferred_username = (0, kit_1.parseJsonMap)(response.body).Username;
|
|
821
817
|
}
|
|
822
818
|
return { username: userInfoJson.preferred_username, organizationId: userInfoJson.organization_id };
|
|
823
819
|
}
|
|
@@ -833,37 +829,47 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
|
|
|
833
829
|
* @private
|
|
834
830
|
*/
|
|
835
831
|
throwUserGetException(response) {
|
|
836
|
-
var _a;
|
|
837
832
|
let errorMsg = '';
|
|
838
|
-
const bodyAsString =
|
|
833
|
+
const bodyAsString = response.body ?? JSON.stringify({ message: 'UNKNOWN', errorCode: 'UNKNOWN' });
|
|
839
834
|
try {
|
|
840
|
-
const body = kit_1.parseJson(bodyAsString);
|
|
841
|
-
if (ts_types_1.isArray(body)) {
|
|
842
|
-
errorMsg = body
|
|
843
|
-
.map((line) => { var _a; return (_a = ts_types_1.getString(line, 'message')) !== null && _a !== void 0 ? _a : ts_types_1.getString(line, 'errorCode', 'UNKNOWN'); })
|
|
844
|
-
.join(os.EOL);
|
|
835
|
+
const body = (0, kit_1.parseJson)(bodyAsString);
|
|
836
|
+
if ((0, ts_types_1.isArray)(body)) {
|
|
837
|
+
errorMsg = body.map((line) => line.message ?? line.errorCode ?? 'UNKNOWN').join(os.EOL);
|
|
845
838
|
}
|
|
846
839
|
else {
|
|
847
|
-
errorMsg =
|
|
840
|
+
errorMsg = body.message ?? body.errorCode ?? 'UNKNOWN';
|
|
848
841
|
}
|
|
849
842
|
}
|
|
850
843
|
catch (err) {
|
|
851
844
|
errorMsg = `${bodyAsString}`;
|
|
852
845
|
}
|
|
853
|
-
throw new
|
|
846
|
+
throw new sfError_1.SfError(errorMsg);
|
|
854
847
|
}
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
848
|
+
/**
|
|
849
|
+
* Returns `true` if the org is a Dev Hub.
|
|
850
|
+
*
|
|
851
|
+
* Check access to the ScratchOrgInfo object to determine if the org is a dev hub.
|
|
852
|
+
*/
|
|
853
|
+
async determineIfDevHub(instanceUrl, accessToken) {
|
|
854
|
+
// Make a REST call for the ScratchOrgInfo obj directly. Normally this is done via a connection
|
|
855
|
+
// but we don't want to create circular dependencies or lots of snowflakes
|
|
856
|
+
// within this file to support it.
|
|
857
|
+
const apiVersion = 'v51.0'; // hardcoding to v51.0 just for this call is okay.
|
|
858
|
+
const instance = (0, ts_types_1.ensure)(instanceUrl);
|
|
859
|
+
const baseUrl = new sfdcUrl_1.SfdcUrl(instance);
|
|
860
|
+
const scratchOrgInfoUrl = `${baseUrl.toString()}/services/data/${apiVersion}/query?q=SELECT%20Id%20FROM%20ScratchOrgInfo%20limit%201`;
|
|
861
|
+
const headers = Object.assign({ Authorization: `Bearer ${accessToken}` }, connection_1.SFDX_HTTP_HEADERS);
|
|
862
|
+
try {
|
|
863
|
+
const res = await new transport_1.default().httpRequest({ url: scratchOrgInfoUrl, method: 'GET', headers });
|
|
864
|
+
if (res.statusCode >= 400) {
|
|
865
|
+
return false;
|
|
866
|
+
}
|
|
867
|
+
return true;
|
|
868
|
+
}
|
|
869
|
+
catch (err) {
|
|
870
|
+
/* Not a dev hub */
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
867
873
|
}
|
|
868
874
|
}
|
|
869
875
|
exports.AuthInfo = AuthInfo;
|