@salesforce/core 6.5.6 → 6.7.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/lib/config/sandboxProcessCache.d.ts +2 -1
- package/lib/crypto/crypto.d.ts +6 -0
- package/lib/crypto/crypto.js +183 -47
- package/lib/crypto/keyChain.js +1 -1
- package/lib/crypto/keyChainImpl.js +1 -1
- package/lib/exported.d.ts +1 -1
- package/lib/org/org.d.ts +32 -2
- package/lib/org/org.js +78 -11
- package/messages/encryption.md +13 -0
- package/messages/org.md +4 -0
- package/package.json +1 -1
|
@@ -2,8 +2,9 @@ import { SandboxProcessObject, SandboxRequest } from '../org/org';
|
|
|
2
2
|
import { TTLConfig } from './ttlConfig';
|
|
3
3
|
export type SandboxRequestCacheEntry = {
|
|
4
4
|
alias?: string;
|
|
5
|
-
setDefault
|
|
5
|
+
setDefault?: boolean;
|
|
6
6
|
prodOrgUsername: string;
|
|
7
|
+
action: 'Create' | 'Refresh';
|
|
7
8
|
sandboxProcessObject: Partial<SandboxProcessObject>;
|
|
8
9
|
sandboxRequest: Partial<SandboxRequest>;
|
|
9
10
|
tracksSource?: boolean;
|
package/lib/crypto/crypto.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export declare class Crypto extends AsyncOptionalCreatable<CryptoOptions> {
|
|
|
21
21
|
* @ignore
|
|
22
22
|
*/
|
|
23
23
|
constructor(options?: CryptoOptions);
|
|
24
|
+
private static unsetCryptoVersion;
|
|
24
25
|
/**
|
|
25
26
|
* Encrypts text. Returns the encrypted string or undefined if no string was passed.
|
|
26
27
|
*
|
|
@@ -45,10 +46,15 @@ export declare class Crypto extends AsyncOptionalCreatable<CryptoOptions> {
|
|
|
45
46
|
* Clears the crypto state. This should be called in a finally block.
|
|
46
47
|
*/
|
|
47
48
|
close(): void;
|
|
49
|
+
isV2Crypto(): boolean;
|
|
48
50
|
/**
|
|
49
51
|
* Initialize async components.
|
|
50
52
|
*/
|
|
51
53
|
protected init(): Promise<void>;
|
|
54
|
+
private encryptV1;
|
|
55
|
+
private encryptV2;
|
|
56
|
+
private decryptV1;
|
|
57
|
+
private decryptV2;
|
|
52
58
|
private getKeyChain;
|
|
53
59
|
}
|
|
54
60
|
export {};
|
package/lib/crypto/crypto.js
CHANGED
|
@@ -37,23 +37,96 @@ const node_path_1 = require("node:path");
|
|
|
37
37
|
const ts_types_1 = require("@salesforce/ts-types");
|
|
38
38
|
const kit_1 = require("@salesforce/kit");
|
|
39
39
|
const logger_1 = require("../logger/logger");
|
|
40
|
+
const lifecycleEvents_1 = require("../lifecycleEvents");
|
|
40
41
|
const messages_1 = require("../messages");
|
|
41
42
|
const cache_1 = require("../util/cache");
|
|
42
43
|
const global_1 = require("../global");
|
|
44
|
+
const sfError_1 = require("../sfError");
|
|
43
45
|
const keyChain_1 = require("./keyChain");
|
|
44
46
|
const secureBuffer_1 = require("./secureBuffer");
|
|
45
47
|
const TAG_DELIMITER = ':';
|
|
46
|
-
const
|
|
48
|
+
const IV_BYTES = {
|
|
49
|
+
v1: 6,
|
|
50
|
+
v2: 12,
|
|
51
|
+
};
|
|
52
|
+
const ENCODING = {
|
|
53
|
+
v1: 'utf8',
|
|
54
|
+
v2: 'hex',
|
|
55
|
+
};
|
|
56
|
+
const KEY_SIZE = {
|
|
57
|
+
v1: 16,
|
|
58
|
+
v2: 32,
|
|
59
|
+
};
|
|
47
60
|
const ALGO = 'aes-256-gcm';
|
|
48
61
|
const AUTH_TAG_LENGTH = 32;
|
|
49
62
|
const ENCRYPTED_CHARS = /[a-f0-9]/;
|
|
50
63
|
const KEY_NAME = 'sfdx';
|
|
51
64
|
const ACCOUNT = 'local';
|
|
65
|
+
let cryptoLogger;
|
|
66
|
+
const getCryptoLogger = () => {
|
|
67
|
+
cryptoLogger ??= logger_1.Logger.childFromRoot('crypto');
|
|
68
|
+
return cryptoLogger;
|
|
69
|
+
};
|
|
70
|
+
const getCryptoV2EnvVar = () => {
|
|
71
|
+
let sfCryptoV2 = process.env.SF_CRYPTO_V2?.toLowerCase();
|
|
72
|
+
if (sfCryptoV2 !== undefined) {
|
|
73
|
+
getCryptoLogger().debug(`SF_CRYPTO_V2=${sfCryptoV2}`);
|
|
74
|
+
// normalize all values that aren't "true" to be "false"
|
|
75
|
+
if (sfCryptoV2 !== 'true') {
|
|
76
|
+
sfCryptoV2 = 'false';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return sfCryptoV2;
|
|
80
|
+
};
|
|
81
|
+
let cryptoVersion;
|
|
82
|
+
const getCryptoVersion = () => {
|
|
83
|
+
if (!cryptoVersion) {
|
|
84
|
+
// This only happens when generating a new key, so use the env var
|
|
85
|
+
// and (for now) default to 'v1'.
|
|
86
|
+
cryptoVersion = getCryptoV2EnvVar() === 'true' ? 'v2' : 'v1';
|
|
87
|
+
}
|
|
88
|
+
return cryptoVersion;
|
|
89
|
+
};
|
|
90
|
+
// Detect the crypto version based on the password (key) length.
|
|
91
|
+
// This happens once per process.
|
|
92
|
+
const detectCryptoVersion = (pwd) => {
|
|
93
|
+
if (!cryptoVersion) {
|
|
94
|
+
// check the env var to see if it's set
|
|
95
|
+
const sfCryptoV2 = getCryptoV2EnvVar();
|
|
96
|
+
// Password length of 64 is v2 crypto and uses hex encoding.
|
|
97
|
+
// Password length of 32 is v1 crypto and uses utf8 encoding.
|
|
98
|
+
if (pwd?.length === KEY_SIZE.v2 * 2) {
|
|
99
|
+
cryptoVersion = 'v2';
|
|
100
|
+
getCryptoLogger().debug('Using v2 crypto');
|
|
101
|
+
if (sfCryptoV2 === 'false') {
|
|
102
|
+
getCryptoLogger().warn(messages.getMessage('v1CryptoWithV2KeyWarning'));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else if (pwd?.length === KEY_SIZE.v1 * 2) {
|
|
106
|
+
cryptoVersion = 'v1';
|
|
107
|
+
getCryptoLogger().debug('Using v1 crypto');
|
|
108
|
+
if (sfCryptoV2 === 'true') {
|
|
109
|
+
getCryptoLogger().warn(messages.getMessage('v2CryptoWithV1KeyWarning'));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
getCryptoLogger().debug("crypto key doesn't match v1 or v2. using SF_CRYPTO_V2.");
|
|
114
|
+
getCryptoVersion();
|
|
115
|
+
}
|
|
116
|
+
void lifecycleEvents_1.Lifecycle.getInstance().emitTelemetry({
|
|
117
|
+
eventName: 'crypto_version',
|
|
118
|
+
library: 'sfdx-core',
|
|
119
|
+
function: 'detectCryptoVersion',
|
|
120
|
+
cryptoVersion, // 'v1' or 'v2'
|
|
121
|
+
cryptoEnvVar: sfCryptoV2, // 'true' or 'false' or 'undefined'
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
};
|
|
52
125
|
;
|
|
53
|
-
const messages = new messages_1.Messages('@salesforce/core', 'encryption', new Map([["invalidEncryptedFormatError", "The encrypted data is not properly formatted."], ["invalidEncryptedFormatError.actions", ["If attempting to create a scratch org then re-authorize. Otherwise create a new scratch org."]], ["authDecryptError", "Failed to decipher auth data. reason: %s."], ["unsupportedOperatingSystemError", "Unsupported Operating System: %s"], ["missingCredentialProgramError", "Unable to find required security software: %s"], ["credentialProgramAccessError", "Unable to execute security software: %s"], ["passwordRetryError", "Failed to get the password after %i retries."], ["passwordRequiredError", "A password is required."], ["keyChainServiceRequiredError", "Unable to get or set a keychain value without a service name."], ["keyChainAccountRequiredError", "Unable to get or set a keychain value without an account name."], ["keyChainUserCanceledError", "User canceled authentication."], ["keychainPasswordCreationError", "Failed to create a password in the keychain."], ["genericKeychainServiceError", "The service and account specified in %s do not match the version of the toolbelt."], ["genericKeychainServiceError.actions", ["Check your toolbelt version and re-auth."]], ["genericKeychainInvalidPermsError", "Invalid file permissions for secret file"], ["genericKeychainInvalidPermsError.actions", ["Ensure the file %s has the file permission octal value of %s."]], ["passwordNotFoundError", "Could not find password.\n%s"], ["passwordNotFoundError.actions", ["Ensure a valid password is returned with the following command: [%s]"]], ["setCredentialError", "Command failed with response:\n%s"], ["setCredentialError.actions", ["Determine why this command failed to set an encryption key for user %s: [%s]."]], ["macKeychainOutOfSync", "We\u2019ve encountered an error with the Mac keychain being out of sync with your `sfdx` credentials. To fix the problem, sync your credentials by authenticating into your org again using the auth commands."]]));
|
|
54
|
-
const makeSecureBuffer = (password) => {
|
|
126
|
+
const messages = new messages_1.Messages('@salesforce/core', 'encryption', new Map([["invalidEncryptedFormatError", "The encrypted data is not properly formatted."], ["invalidEncryptedFormatError.actions", ["If attempting to create a scratch org then re-authorize. Otherwise create a new scratch org."]], ["authDecryptError", "Failed to decipher auth data. reason: %s."], ["unsupportedOperatingSystemError", "Unsupported Operating System: %s"], ["missingCredentialProgramError", "Unable to find required security software: %s"], ["credentialProgramAccessError", "Unable to execute security software: %s"], ["passwordRetryError", "Failed to get the password after %i retries."], ["passwordRequiredError", "A password is required."], ["keyChainServiceRequiredError", "Unable to get or set a keychain value without a service name."], ["keyChainAccountRequiredError", "Unable to get or set a keychain value without an account name."], ["keyChainUserCanceledError", "User canceled authentication."], ["keychainPasswordCreationError", "Failed to create a password in the keychain."], ["genericKeychainServiceError", "The service and account specified in %s do not match the version of the toolbelt."], ["genericKeychainServiceError.actions", ["Check your toolbelt version and re-auth."]], ["genericKeychainInvalidPermsError", "Invalid file permissions for secret file"], ["genericKeychainInvalidPermsError.actions", ["Ensure the file %s has the file permission octal value of %s."]], ["passwordNotFoundError", "Could not find password.\n%s"], ["passwordNotFoundError.actions", ["Ensure a valid password is returned with the following command: [%s]"]], ["setCredentialError", "Command failed with response:\n%s"], ["setCredentialError.actions", ["Determine why this command failed to set an encryption key for user %s: [%s]."]], ["macKeychainOutOfSync", "We\u2019ve encountered an error with the Mac keychain being out of sync with your `sfdx` credentials. To fix the problem, sync your credentials by authenticating into your org again using the auth commands."], ["v1CryptoWithV2KeyWarning", "The SF_CRYPTO_V2 environment variable was set to \"false\" but a v2 crypto key was detected. v1 crypto can only be used with a v1 key. Unset the SF_CRYPTO_V2 environment variable."], ["v2CryptoWithV1KeyWarning", "SF_CRYPTO_V2 was set to \"true\" but a v1 crypto key was detected. v2 crypto can only be used with a v2 key. To generate a v2 key:\n\n1. Logout of all orgs: `sf org logout --all`\n2. Delete the sfdx keychain entry (account: local, service: sfdx). If `SF_USE_GENERIC_UNIX_KEYCHAIN=true` env var is set, you can delete the `key.json` file.\n3. Set `SF_CRYPTO_V2=true` env var.\n4. Re-Authenticate with your orgs using the CLI org login commands."]]));
|
|
127
|
+
const makeSecureBuffer = (password, encoding) => {
|
|
55
128
|
const newSb = new secureBuffer_1.SecureBuffer();
|
|
56
|
-
newSb.consume(Buffer.from(
|
|
129
|
+
newSb.consume(Buffer.from(password, encoding));
|
|
57
130
|
return newSb;
|
|
58
131
|
};
|
|
59
132
|
/**
|
|
@@ -74,14 +147,19 @@ const keychainPromises = {
|
|
|
74
147
|
return new Promise((resolve, reject) => _keychain.getPassword({ service, account }, (err, password) => {
|
|
75
148
|
if (err)
|
|
76
149
|
return reject(err);
|
|
77
|
-
|
|
78
|
-
|
|
150
|
+
const pwd = (0, ts_types_1.ensure)(password, 'Expected the keychain password to be set');
|
|
151
|
+
detectCryptoVersion(pwd);
|
|
152
|
+
cache_1.Cache.set(cacheKey, makeSecureBuffer(pwd, ENCODING[getCryptoVersion()]));
|
|
153
|
+
return resolve({ username: account, password: pwd });
|
|
79
154
|
}));
|
|
80
155
|
}
|
|
81
156
|
else {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
157
|
+
// If the password is cached, we know the crypto version and encoding because it was
|
|
158
|
+
// detected by the non-cache code path just above this.
|
|
159
|
+
const encoding = ENCODING[getCryptoVersion()];
|
|
160
|
+
const pwd = (0, ts_types_1.ensure)(sb.value((buffer) => buffer.toString(encoding)), 'Expected the keychain password to be set');
|
|
161
|
+
cache_1.Cache.set(cacheKey, makeSecureBuffer(pwd, encoding));
|
|
162
|
+
return new Promise((resolve) => resolve({ username: account, password: pwd }));
|
|
85
163
|
}
|
|
86
164
|
},
|
|
87
165
|
/**
|
|
@@ -116,6 +194,11 @@ class Crypto extends kit_1.AsyncOptionalCreatable {
|
|
|
116
194
|
this.key = new secureBuffer_1.SecureBuffer();
|
|
117
195
|
this.options = options ?? {};
|
|
118
196
|
}
|
|
197
|
+
// @ts-expect-error only for test access
|
|
198
|
+
// eslint-disable-next-line class-methods-use-this
|
|
199
|
+
static unsetCryptoVersion() {
|
|
200
|
+
cryptoVersion = undefined;
|
|
201
|
+
}
|
|
119
202
|
encrypt(text) {
|
|
120
203
|
if (text == null) {
|
|
121
204
|
return;
|
|
@@ -123,14 +206,13 @@ class Crypto extends kit_1.AsyncOptionalCreatable {
|
|
|
123
206
|
if (this.key == null) {
|
|
124
207
|
throw messages.createError('keychainPasswordCreationError');
|
|
125
208
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
});
|
|
209
|
+
// When everything is v2, we can remove the else
|
|
210
|
+
if (this.isV2Crypto()) {
|
|
211
|
+
return this.encryptV2(text);
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
return this.encryptV1(text);
|
|
215
|
+
}
|
|
134
216
|
}
|
|
135
217
|
decrypt(text) {
|
|
136
218
|
if (text == null) {
|
|
@@ -140,27 +222,13 @@ class Crypto extends kit_1.AsyncOptionalCreatable {
|
|
|
140
222
|
if (tokens.length !== 2) {
|
|
141
223
|
throw messages.createError('invalidEncryptedFormatError');
|
|
142
224
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
decipher.setAuthTag(Buffer.from(tag, 'hex'));
|
|
151
|
-
dec = decipher.update(secret, 'hex', 'utf8');
|
|
152
|
-
dec += decipher.final('utf8');
|
|
153
|
-
}
|
|
154
|
-
catch (err) {
|
|
155
|
-
const error = messages.createError('authDecryptError', [err.message], [], err);
|
|
156
|
-
const useGenericUnixKeychain = kit_1.env.getBoolean('SF_USE_GENERIC_UNIX_KEYCHAIN') || kit_1.env.getBoolean('USE_GENERIC_UNIX_KEYCHAIN');
|
|
157
|
-
if (os.platform() === 'darwin' && !useGenericUnixKeychain) {
|
|
158
|
-
error.actions = [messages.getMessage('macKeychainOutOfSync')];
|
|
159
|
-
}
|
|
160
|
-
throw error;
|
|
161
|
-
}
|
|
162
|
-
return dec;
|
|
163
|
-
});
|
|
225
|
+
// When everything is v2, we can remove the else
|
|
226
|
+
if (this.isV2Crypto()) {
|
|
227
|
+
return this.decryptV2(tokens);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
return this.decryptV1(tokens);
|
|
231
|
+
}
|
|
164
232
|
}
|
|
165
233
|
/**
|
|
166
234
|
* Takes a best guess if the value provided was encrypted by {@link Crypto.encrypt} by
|
|
@@ -181,7 +249,7 @@ class Crypto extends kit_1.AsyncOptionalCreatable {
|
|
|
181
249
|
const tag = tokens[1];
|
|
182
250
|
const value = tokens[0];
|
|
183
251
|
return (tag.length === AUTH_TAG_LENGTH &&
|
|
184
|
-
value.length >=
|
|
252
|
+
value.length >= IV_BYTES[getCryptoVersion()] &&
|
|
185
253
|
ENCRYPTED_CHARS.test(tag) &&
|
|
186
254
|
ENCRYPTED_CHARS.test(tokens[0]));
|
|
187
255
|
}
|
|
@@ -193,33 +261,39 @@ class Crypto extends kit_1.AsyncOptionalCreatable {
|
|
|
193
261
|
this.key.clear();
|
|
194
262
|
}
|
|
195
263
|
}
|
|
264
|
+
// eslint-disable-next-line class-methods-use-this
|
|
265
|
+
isV2Crypto() {
|
|
266
|
+
return getCryptoVersion() === 'v2';
|
|
267
|
+
}
|
|
196
268
|
/**
|
|
197
269
|
* Initialize async components.
|
|
198
270
|
*/
|
|
199
271
|
async init() {
|
|
200
|
-
const logger = await logger_1.Logger.child('crypto');
|
|
201
272
|
if (!this.options.platform) {
|
|
202
273
|
this.options.platform = os.platform();
|
|
203
274
|
}
|
|
204
|
-
logger.debug(`retryStatus: ${this.options.retryStatus}`);
|
|
205
275
|
this.noResetOnClose = !!this.options.noResetOnClose;
|
|
206
276
|
try {
|
|
207
|
-
|
|
208
|
-
|
|
277
|
+
const keyChain = await this.getKeyChain(this.options.platform);
|
|
278
|
+
const pwd = (await keychainPromises.getPassword(keyChain, KEY_NAME, ACCOUNT)).password;
|
|
279
|
+
// The above line ensures the crypto version is detected and set so we can rely on it now.
|
|
280
|
+
this.key.consume(Buffer.from(pwd, ENCODING[getCryptoVersion()]));
|
|
209
281
|
}
|
|
210
282
|
catch (err) {
|
|
211
283
|
// No password found
|
|
212
284
|
if (err.name === 'PasswordNotFoundError') {
|
|
213
285
|
// If we already tried to create a new key then bail.
|
|
214
286
|
if (this.options.retryStatus === 'KEY_SET') {
|
|
215
|
-
|
|
287
|
+
getCryptoLogger().debug('a key was set but the retry to get the password failed.');
|
|
216
288
|
throw err;
|
|
217
289
|
}
|
|
218
290
|
else {
|
|
219
|
-
|
|
291
|
+
getCryptoLogger().debug(`password not found in keychain. Creating new one (Crypto ${getCryptoVersion()}) and re-init.`);
|
|
220
292
|
}
|
|
221
|
-
|
|
222
|
-
//
|
|
293
|
+
// 2/6/2024: This generates a new key using the crypto version based on the SF_CRYPTO_V2 env var.
|
|
294
|
+
// Sometime in the future we could hardcode this to be `KEY_SIZE.v2` so that it becomes the default.
|
|
295
|
+
const key = crypto.randomBytes(KEY_SIZE[getCryptoVersion()]).toString('hex');
|
|
296
|
+
// Set the new password in the KeyChain.
|
|
223
297
|
await keychainPromises.setPassword((0, ts_types_1.ensure)(this.options.keychain), KEY_NAME, ACCOUNT, key);
|
|
224
298
|
return this.init();
|
|
225
299
|
}
|
|
@@ -228,6 +302,68 @@ class Crypto extends kit_1.AsyncOptionalCreatable {
|
|
|
228
302
|
}
|
|
229
303
|
}
|
|
230
304
|
}
|
|
305
|
+
encryptV1(text) {
|
|
306
|
+
const iv = crypto.randomBytes(IV_BYTES.v1).toString('hex');
|
|
307
|
+
return this.key.value((buffer) => {
|
|
308
|
+
const cipher = crypto.createCipheriv(ALGO, buffer.toString('utf8'), iv);
|
|
309
|
+
let encrypted = cipher.update(text, 'utf8', 'hex');
|
|
310
|
+
encrypted += cipher.final('hex');
|
|
311
|
+
const tag = cipher.getAuthTag().toString('hex');
|
|
312
|
+
return `${iv}${encrypted}${TAG_DELIMITER}${tag}`;
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
encryptV2(text) {
|
|
316
|
+
const iv = crypto.randomBytes(IV_BYTES.v2);
|
|
317
|
+
return this.key.value((buffer) => {
|
|
318
|
+
const cipher = crypto.createCipheriv(ALGO, buffer, iv);
|
|
319
|
+
const ivHex = iv.toString('hex');
|
|
320
|
+
let encrypted = cipher.update(text, 'utf8', 'hex');
|
|
321
|
+
encrypted += cipher.final('hex');
|
|
322
|
+
const tag = cipher.getAuthTag().toString('hex');
|
|
323
|
+
return `${ivHex}${encrypted}${TAG_DELIMITER}${tag}`;
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
decryptV1(tokens) {
|
|
327
|
+
const tag = tokens[1];
|
|
328
|
+
const iv = tokens[0].substring(0, IV_BYTES.v1 * 2);
|
|
329
|
+
const secret = tokens[0].substring(IV_BYTES.v1 * 2, tokens[0].length);
|
|
330
|
+
return this.key.value((buffer) => {
|
|
331
|
+
const decipher = crypto.createDecipheriv(ALGO, buffer.toString('utf8'), iv);
|
|
332
|
+
try {
|
|
333
|
+
decipher.setAuthTag(Buffer.from(tag, 'hex'));
|
|
334
|
+
return `${decipher.update(secret, 'hex', 'utf8')}${decipher.final('utf8')}`;
|
|
335
|
+
}
|
|
336
|
+
catch (err) {
|
|
337
|
+
const error = messages.createError('authDecryptError', [err.message], [], err);
|
|
338
|
+
const useGenericUnixKeychain = kit_1.env.getBoolean('SF_USE_GENERIC_UNIX_KEYCHAIN') || kit_1.env.getBoolean('USE_GENERIC_UNIX_KEYCHAIN');
|
|
339
|
+
if (os.platform() === 'darwin' && !useGenericUnixKeychain) {
|
|
340
|
+
error.actions = [messages.getMessage('macKeychainOutOfSync')];
|
|
341
|
+
}
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
decryptV2(tokens) {
|
|
347
|
+
const tag = tokens[1];
|
|
348
|
+
const iv = tokens[0].substring(0, IV_BYTES.v2 * 2);
|
|
349
|
+
const secret = tokens[0].substring(IV_BYTES.v2 * 2, tokens[0].length);
|
|
350
|
+
return this.key.value((buffer) => {
|
|
351
|
+
const decipher = crypto.createDecipheriv(ALGO, buffer, Buffer.from(iv, 'hex'));
|
|
352
|
+
try {
|
|
353
|
+
decipher.setAuthTag(Buffer.from(tag, 'hex'));
|
|
354
|
+
return `${decipher.update(secret, 'hex', 'utf8')}${decipher.final('utf8')}`;
|
|
355
|
+
}
|
|
356
|
+
catch (_err) {
|
|
357
|
+
const err = ((0, ts_types_1.isString)(_err) ? sfError_1.SfError.wrap(_err) : _err);
|
|
358
|
+
const error = messages.createError('authDecryptError', [err.message], [], err);
|
|
359
|
+
const useGenericUnixKeychain = kit_1.env.getBoolean('SF_USE_GENERIC_UNIX_KEYCHAIN') || kit_1.env.getBoolean('USE_GENERIC_UNIX_KEYCHAIN');
|
|
360
|
+
if (os.platform() === 'darwin' && !useGenericUnixKeychain) {
|
|
361
|
+
error.actions = [messages.getMessage('macKeychainOutOfSync')];
|
|
362
|
+
}
|
|
363
|
+
throw error;
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
}
|
|
231
367
|
async getKeyChain(platform) {
|
|
232
368
|
if (!this.options.keychain) {
|
|
233
369
|
this.options.keychain = await (0, keyChain_1.retrieveKeychain)(platform);
|
package/lib/crypto/keyChain.js
CHANGED
|
@@ -12,7 +12,7 @@ const logger_1 = require("../logger/logger");
|
|
|
12
12
|
const messages_1 = require("../messages");
|
|
13
13
|
const keyChainImpl_1 = require("./keyChainImpl");
|
|
14
14
|
;
|
|
15
|
-
const messages = new messages_1.Messages('@salesforce/core', 'encryption', new Map([["invalidEncryptedFormatError", "The encrypted data is not properly formatted."], ["invalidEncryptedFormatError.actions", ["If attempting to create a scratch org then re-authorize. Otherwise create a new scratch org."]], ["authDecryptError", "Failed to decipher auth data. reason: %s."], ["unsupportedOperatingSystemError", "Unsupported Operating System: %s"], ["missingCredentialProgramError", "Unable to find required security software: %s"], ["credentialProgramAccessError", "Unable to execute security software: %s"], ["passwordRetryError", "Failed to get the password after %i retries."], ["passwordRequiredError", "A password is required."], ["keyChainServiceRequiredError", "Unable to get or set a keychain value without a service name."], ["keyChainAccountRequiredError", "Unable to get or set a keychain value without an account name."], ["keyChainUserCanceledError", "User canceled authentication."], ["keychainPasswordCreationError", "Failed to create a password in the keychain."], ["genericKeychainServiceError", "The service and account specified in %s do not match the version of the toolbelt."], ["genericKeychainServiceError.actions", ["Check your toolbelt version and re-auth."]], ["genericKeychainInvalidPermsError", "Invalid file permissions for secret file"], ["genericKeychainInvalidPermsError.actions", ["Ensure the file %s has the file permission octal value of %s."]], ["passwordNotFoundError", "Could not find password.\n%s"], ["passwordNotFoundError.actions", ["Ensure a valid password is returned with the following command: [%s]"]], ["setCredentialError", "Command failed with response:\n%s"], ["setCredentialError.actions", ["Determine why this command failed to set an encryption key for user %s: [%s]."]], ["macKeychainOutOfSync", "We\u2019ve encountered an error with the Mac keychain being out of sync with your `sfdx` credentials. To fix the problem, sync your credentials by authenticating into your org again using the auth commands."]]));
|
|
15
|
+
const messages = new messages_1.Messages('@salesforce/core', 'encryption', new Map([["invalidEncryptedFormatError", "The encrypted data is not properly formatted."], ["invalidEncryptedFormatError.actions", ["If attempting to create a scratch org then re-authorize. Otherwise create a new scratch org."]], ["authDecryptError", "Failed to decipher auth data. reason: %s."], ["unsupportedOperatingSystemError", "Unsupported Operating System: %s"], ["missingCredentialProgramError", "Unable to find required security software: %s"], ["credentialProgramAccessError", "Unable to execute security software: %s"], ["passwordRetryError", "Failed to get the password after %i retries."], ["passwordRequiredError", "A password is required."], ["keyChainServiceRequiredError", "Unable to get or set a keychain value without a service name."], ["keyChainAccountRequiredError", "Unable to get or set a keychain value without an account name."], ["keyChainUserCanceledError", "User canceled authentication."], ["keychainPasswordCreationError", "Failed to create a password in the keychain."], ["genericKeychainServiceError", "The service and account specified in %s do not match the version of the toolbelt."], ["genericKeychainServiceError.actions", ["Check your toolbelt version and re-auth."]], ["genericKeychainInvalidPermsError", "Invalid file permissions for secret file"], ["genericKeychainInvalidPermsError.actions", ["Ensure the file %s has the file permission octal value of %s."]], ["passwordNotFoundError", "Could not find password.\n%s"], ["passwordNotFoundError.actions", ["Ensure a valid password is returned with the following command: [%s]"]], ["setCredentialError", "Command failed with response:\n%s"], ["setCredentialError.actions", ["Determine why this command failed to set an encryption key for user %s: [%s]."]], ["macKeychainOutOfSync", "We\u2019ve encountered an error with the Mac keychain being out of sync with your `sfdx` credentials. To fix the problem, sync your credentials by authenticating into your org again using the auth commands."], ["v1CryptoWithV2KeyWarning", "The SF_CRYPTO_V2 environment variable was set to \"false\" but a v2 crypto key was detected. v1 crypto can only be used with a v1 key. Unset the SF_CRYPTO_V2 environment variable."], ["v2CryptoWithV1KeyWarning", "SF_CRYPTO_V2 was set to \"true\" but a v1 crypto key was detected. v2 crypto can only be used with a v2 key. To generate a v2 key:\n\n1. Logout of all orgs: `sf org logout --all`\n2. Delete the sfdx keychain entry (account: local, service: sfdx). If `SF_USE_GENERIC_UNIX_KEYCHAIN=true` env var is set, you can delete the `key.json` file.\n3. Set `SF_CRYPTO_V2=true` env var.\n4. Re-Authenticate with your orgs using the CLI org login commands."]]));
|
|
16
16
|
/**
|
|
17
17
|
* Gets the os level keychain impl.
|
|
18
18
|
*
|
|
@@ -41,7 +41,7 @@ const kit_1 = require("@salesforce/kit");
|
|
|
41
41
|
const global_1 = require("../global");
|
|
42
42
|
const messages_1 = require("../messages");
|
|
43
43
|
;
|
|
44
|
-
const messages = new messages_1.Messages('@salesforce/core', 'encryption', new Map([["invalidEncryptedFormatError", "The encrypted data is not properly formatted."], ["invalidEncryptedFormatError.actions", ["If attempting to create a scratch org then re-authorize. Otherwise create a new scratch org."]], ["authDecryptError", "Failed to decipher auth data. reason: %s."], ["unsupportedOperatingSystemError", "Unsupported Operating System: %s"], ["missingCredentialProgramError", "Unable to find required security software: %s"], ["credentialProgramAccessError", "Unable to execute security software: %s"], ["passwordRetryError", "Failed to get the password after %i retries."], ["passwordRequiredError", "A password is required."], ["keyChainServiceRequiredError", "Unable to get or set a keychain value without a service name."], ["keyChainAccountRequiredError", "Unable to get or set a keychain value without an account name."], ["keyChainUserCanceledError", "User canceled authentication."], ["keychainPasswordCreationError", "Failed to create a password in the keychain."], ["genericKeychainServiceError", "The service and account specified in %s do not match the version of the toolbelt."], ["genericKeychainServiceError.actions", ["Check your toolbelt version and re-auth."]], ["genericKeychainInvalidPermsError", "Invalid file permissions for secret file"], ["genericKeychainInvalidPermsError.actions", ["Ensure the file %s has the file permission octal value of %s."]], ["passwordNotFoundError", "Could not find password.\n%s"], ["passwordNotFoundError.actions", ["Ensure a valid password is returned with the following command: [%s]"]], ["setCredentialError", "Command failed with response:\n%s"], ["setCredentialError.actions", ["Determine why this command failed to set an encryption key for user %s: [%s]."]], ["macKeychainOutOfSync", "We\u2019ve encountered an error with the Mac keychain being out of sync with your `sfdx` credentials. To fix the problem, sync your credentials by authenticating into your org again using the auth commands."]]));
|
|
44
|
+
const messages = new messages_1.Messages('@salesforce/core', 'encryption', new Map([["invalidEncryptedFormatError", "The encrypted data is not properly formatted."], ["invalidEncryptedFormatError.actions", ["If attempting to create a scratch org then re-authorize. Otherwise create a new scratch org."]], ["authDecryptError", "Failed to decipher auth data. reason: %s."], ["unsupportedOperatingSystemError", "Unsupported Operating System: %s"], ["missingCredentialProgramError", "Unable to find required security software: %s"], ["credentialProgramAccessError", "Unable to execute security software: %s"], ["passwordRetryError", "Failed to get the password after %i retries."], ["passwordRequiredError", "A password is required."], ["keyChainServiceRequiredError", "Unable to get or set a keychain value without a service name."], ["keyChainAccountRequiredError", "Unable to get or set a keychain value without an account name."], ["keyChainUserCanceledError", "User canceled authentication."], ["keychainPasswordCreationError", "Failed to create a password in the keychain."], ["genericKeychainServiceError", "The service and account specified in %s do not match the version of the toolbelt."], ["genericKeychainServiceError.actions", ["Check your toolbelt version and re-auth."]], ["genericKeychainInvalidPermsError", "Invalid file permissions for secret file"], ["genericKeychainInvalidPermsError.actions", ["Ensure the file %s has the file permission octal value of %s."]], ["passwordNotFoundError", "Could not find password.\n%s"], ["passwordNotFoundError.actions", ["Ensure a valid password is returned with the following command: [%s]"]], ["setCredentialError", "Command failed with response:\n%s"], ["setCredentialError.actions", ["Determine why this command failed to set an encryption key for user %s: [%s]."]], ["macKeychainOutOfSync", "We\u2019ve encountered an error with the Mac keychain being out of sync with your `sfdx` credentials. To fix the problem, sync your credentials by authenticating into your org again using the auth commands."], ["v1CryptoWithV2KeyWarning", "The SF_CRYPTO_V2 environment variable was set to \"false\" but a v2 crypto key was detected. v1 crypto can only be used with a v1 key. Unset the SF_CRYPTO_V2 environment variable."], ["v2CryptoWithV1KeyWarning", "SF_CRYPTO_V2 was set to \"true\" but a v1 crypto key was detected. v2 crypto can only be used with a v2 key. To generate a v2 key:\n\n1. Logout of all orgs: `sf org logout --all`\n2. Delete the sfdx keychain entry (account: local, service: sfdx). If `SF_USE_GENERIC_UNIX_KEYCHAIN=true` env var is set, you can delete the `key.json` file.\n3. Set `SF_CRYPTO_V2=true` env var.\n4. Re-Authenticate with your orgs using the CLI org login commands."]]));
|
|
45
45
|
const GET_PASSWORD_RETRY_COUNT = 3;
|
|
46
46
|
/**
|
|
47
47
|
* Helper to reduce an array of cli args down to a presentable string for logging.
|
package/lib/exported.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export { SfdcUrl } from './util/sfdcUrl';
|
|
|
20
20
|
export { getJwtAudienceUrl } from './util/getJwtAudienceUrl';
|
|
21
21
|
export { Fields, FieldValue, LoggerLevel, LoggerLevelValue, LogLine, LoggerOptions, Logger } from './logger/logger';
|
|
22
22
|
export { Messages, StructuredMessage } from './messages';
|
|
23
|
-
export { Org, SandboxProcessObject, StatusEvent, SandboxEvents, SandboxUserAuthResponse, SandboxUserAuthRequest, SandboxRequest, ResumeSandboxRequest, OrgTypes, ResultEvent, ScratchOrgRequest, } from './org/org';
|
|
23
|
+
export { Org, SandboxProcessObject, StatusEvent, SandboxInfo, SandboxEvents, SandboxUserAuthResponse, SandboxUserAuthRequest, SandboxRequest, ResumeSandboxRequest, OrgTypes, ResultEvent, ScratchOrgRequest, } from './org/org';
|
|
24
24
|
export { OrgConfigProperties, ORG_CONFIG_ALLOWED_PROPERTIES } from './org/orgConfigProperties';
|
|
25
25
|
export { PackageDir, NamedPackageDir, PackageDirDependency, SfProject, SfProjectJson } from './sfProject';
|
|
26
26
|
export { SchemaValidator } from './schema/validator';
|
package/lib/org/org.d.ts
CHANGED
|
@@ -71,6 +71,24 @@ export type ResumeSandboxRequest = {
|
|
|
71
71
|
SandboxName?: string;
|
|
72
72
|
SandboxProcessObjId?: string;
|
|
73
73
|
};
|
|
74
|
+
export type SandboxInfo = {
|
|
75
|
+
Id: string;
|
|
76
|
+
IsDeleted: boolean;
|
|
77
|
+
CreatedDate: string;
|
|
78
|
+
CreatedById: string;
|
|
79
|
+
LastModifiedDate: string;
|
|
80
|
+
LastModifiedById: string;
|
|
81
|
+
SandboxName: string;
|
|
82
|
+
LicenseType: 'DEVELOPER' | 'DEVELOPER PRO' | 'PARTIAL' | 'FULL';
|
|
83
|
+
TemplateId?: string;
|
|
84
|
+
HistoryDays: -1 | 0 | 10 | 20 | 30 | 60 | 90 | 120 | 150 | 180;
|
|
85
|
+
CopyChatter: boolean;
|
|
86
|
+
AutoActivate: boolean;
|
|
87
|
+
ApexClassId?: string;
|
|
88
|
+
Description?: string;
|
|
89
|
+
SourceId?: string;
|
|
90
|
+
CopyArchivedActivities?: boolean;
|
|
91
|
+
};
|
|
74
92
|
export type ScratchOrgRequest = Omit<ScratchOrgCreateOptions, 'hubOrg'>;
|
|
75
93
|
export type SandboxFields = {
|
|
76
94
|
sandboxOrgId: string;
|
|
@@ -130,6 +148,18 @@ export declare class Org extends AsyncOptionalCreatable<Org.Options> {
|
|
|
130
148
|
interval?: Duration;
|
|
131
149
|
async?: boolean;
|
|
132
150
|
}): Promise<SandboxProcessObject>;
|
|
151
|
+
/**
|
|
152
|
+
* Refresh (update) a sandbox from a production org.
|
|
153
|
+
* 'this' needs to be a production org with sandbox licenses available
|
|
154
|
+
*
|
|
155
|
+
* @param sandboxInfo SandboxInfo to update the sandbox with
|
|
156
|
+
* @param options Wait: The amount of time to wait before timing out, Interval: The time interval between polling
|
|
157
|
+
*/
|
|
158
|
+
refreshSandbox(sandboxInfo: SandboxInfo, options?: {
|
|
159
|
+
wait?: Duration;
|
|
160
|
+
interval?: Duration;
|
|
161
|
+
async?: boolean;
|
|
162
|
+
}): Promise<SandboxProcessObject>;
|
|
133
163
|
/**
|
|
134
164
|
*
|
|
135
165
|
* @param sandboxReq SandboxRequest options to create the sandbox with
|
|
@@ -142,10 +172,10 @@ export declare class Org extends AsyncOptionalCreatable<Org.Options> {
|
|
|
142
172
|
interval?: Duration;
|
|
143
173
|
}): Promise<SandboxProcessObject>;
|
|
144
174
|
/**
|
|
145
|
-
* Resume a sandbox
|
|
175
|
+
* Resume a sandbox create or refresh from a production org.
|
|
146
176
|
* `this` needs to be a production org with sandbox licenses available.
|
|
147
177
|
*
|
|
148
|
-
* @param resumeSandboxRequest SandboxRequest options to create the sandbox with
|
|
178
|
+
* @param resumeSandboxRequest SandboxRequest options to create/refresh the sandbox with
|
|
149
179
|
* @param options Wait: The amount of time to wait (default: 0 minutes) before timing out,
|
|
150
180
|
* Interval: The time interval (default: 30 seconds) between polling
|
|
151
181
|
*/
|
package/lib/org/org.js
CHANGED
|
@@ -52,7 +52,7 @@ const authInfo_1 = require("./authInfo");
|
|
|
52
52
|
const scratchOrgCreate_1 = require("./scratchOrgCreate");
|
|
53
53
|
const orgConfigProperties_1 = require("./orgConfigProperties");
|
|
54
54
|
;
|
|
55
|
-
const messages = new messages_1.Messages('@salesforce/core', 'org', new Map([["notADevHub", "The provided dev hub username %s is not a valid dev hub."], ["noUsernameFound", "No username found."], ["noDevHubFound", "Unable to associate this scratch org with a DevHub."], ["deleteOrgHubError", "The Dev Hub org cannot be deleted."], ["insufficientAccessToDelete", "You do not have the appropriate permissions to delete a scratch org. Please contact your Salesforce admin."], ["scratchOrgNotFound", "Attempting to delete an expired or deleted org"], ["sandboxDeleteFailed", "The sandbox org deletion failed with a result of %s."], ["sandboxNotFound", "We can't find a SandboxProcess for the sandbox %s."], ["sandboxInfoCreateFailed", "The sandbox org creation failed with a result of %s."], ["missingAuthUsername", "The sandbox %s does not have an authorized username."], ["orgPollingTimeout", "Sandbox status is %s; timed out waiting for completion."], ["NotFoundOnDevHub", "The scratch org does not belong to the dev hub username %s."], ["AuthInfoOrgIdUndefined", "AuthInfo orgId is undefined."], ["sandboxCreateNotComplete", "The sandbox creation has not completed."], ["SandboxProcessNotFoundBySandboxName", "We can't find a SandboxProcess with the SandboxName %s."], ["MultiSandboxProcessNotFoundBySandboxName", "We found more than one SandboxProcess with the SandboxName %s."], ["sandboxNotResumable", "The sandbox %s cannot resume with status of %s."]]));
|
|
55
|
+
const messages = new messages_1.Messages('@salesforce/core', 'org', new Map([["notADevHub", "The provided dev hub username %s is not a valid dev hub."], ["noUsernameFound", "No username found."], ["noDevHubFound", "Unable to associate this scratch org with a DevHub."], ["deleteOrgHubError", "The Dev Hub org cannot be deleted."], ["insufficientAccessToDelete", "You do not have the appropriate permissions to delete a scratch org. Please contact your Salesforce admin."], ["scratchOrgNotFound", "Attempting to delete an expired or deleted org"], ["sandboxDeleteFailed", "The sandbox org deletion failed with a result of %s."], ["sandboxNotFound", "We can't find a SandboxProcess for the sandbox %s."], ["sandboxInfoCreateFailed", "The sandbox org creation failed with a result of %s."], ["sandboxInfoRefreshFailed", "The sandbox org refresh failed with a result of %s."], ["missingAuthUsername", "The sandbox %s does not have an authorized username."], ["orgPollingTimeout", "Sandbox status is %s; timed out waiting for completion."], ["NotFoundOnDevHub", "The scratch org does not belong to the dev hub username %s."], ["AuthInfoOrgIdUndefined", "AuthInfo orgId is undefined."], ["sandboxCreateNotComplete", "The sandbox creation has not completed."], ["SandboxProcessNotFoundBySandboxName", "We can't find a SandboxProcess with the SandboxName %s."], ["MultiSandboxProcessNotFoundBySandboxName", "We found more than one SandboxProcess with the SandboxName %s."], ["sandboxNotResumable", "The sandbox %s cannot resume with status of %s."]]));
|
|
56
56
|
var OrgTypes;
|
|
57
57
|
(function (OrgTypes) {
|
|
58
58
|
OrgTypes["Scratch"] = "scratch";
|
|
@@ -72,6 +72,19 @@ function sandboxIsResumable(value) {
|
|
|
72
72
|
return resumableSandboxStatus.includes(value);
|
|
73
73
|
}
|
|
74
74
|
exports.sandboxIsResumable = sandboxIsResumable;
|
|
75
|
+
const sandboxProcessFields = [
|
|
76
|
+
'Id',
|
|
77
|
+
'Status',
|
|
78
|
+
'SandboxName',
|
|
79
|
+
'SandboxInfoId',
|
|
80
|
+
'LicenseType',
|
|
81
|
+
'CreatedDate',
|
|
82
|
+
'CopyProgress',
|
|
83
|
+
'SandboxOrganization',
|
|
84
|
+
'SourceId',
|
|
85
|
+
'Description',
|
|
86
|
+
'EndDate',
|
|
87
|
+
];
|
|
75
88
|
/**
|
|
76
89
|
* Provides a way to manage a locally authenticated Org.
|
|
77
90
|
*
|
|
@@ -141,6 +154,46 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
141
154
|
pollInterval,
|
|
142
155
|
});
|
|
143
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Refresh (update) a sandbox from a production org.
|
|
159
|
+
* 'this' needs to be a production org with sandbox licenses available
|
|
160
|
+
*
|
|
161
|
+
* @param sandboxInfo SandboxInfo to update the sandbox with
|
|
162
|
+
* @param options Wait: The amount of time to wait before timing out, Interval: The time interval between polling
|
|
163
|
+
*/
|
|
164
|
+
async refreshSandbox(sandboxInfo, options = {
|
|
165
|
+
wait: kit_1.Duration.minutes(6),
|
|
166
|
+
async: false,
|
|
167
|
+
interval: kit_1.Duration.seconds(30),
|
|
168
|
+
}) {
|
|
169
|
+
this.logger.debug(sandboxInfo, 'RefreshSandbox called with SandboxInfo');
|
|
170
|
+
const refreshResult = await this.connection.tooling.update('SandboxInfo', sandboxInfo);
|
|
171
|
+
this.logger.debug(refreshResult, 'Return from calling tooling.update');
|
|
172
|
+
if (!refreshResult.success) {
|
|
173
|
+
throw messages.createError('sandboxInfoRefreshFailed', [JSON.stringify(refreshResult)]);
|
|
174
|
+
}
|
|
175
|
+
const soql = `SELECT ${sandboxProcessFields.join(',')} FROM SandboxProcess WHERE SandboxName='${sandboxInfo.SandboxName}' ORDER BY CreatedDate DESC`;
|
|
176
|
+
const sbxProcessObjects = (await this.connection.tooling.query(soql)).records.filter((item) => !item.Status.startsWith('Del'));
|
|
177
|
+
this.logger.debug(sbxProcessObjects, `SandboxProcesses for ${sandboxInfo.SandboxName}`);
|
|
178
|
+
// throw if none found
|
|
179
|
+
if (sbxProcessObjects?.length === 0) {
|
|
180
|
+
throw new Error(`No SandboxProcesses found for: ${sandboxInfo.SandboxName}`);
|
|
181
|
+
}
|
|
182
|
+
const sandboxRefreshProgress = sbxProcessObjects[0];
|
|
183
|
+
const isAsync = !!options.async;
|
|
184
|
+
if (isAsync) {
|
|
185
|
+
// The user didn't want us to poll, so simply return the status
|
|
186
|
+
await lifecycleEvents_1.Lifecycle.getInstance().emit(SandboxEvents.EVENT_ASYNC_RESULT, sandboxRefreshProgress);
|
|
187
|
+
return sandboxRefreshProgress;
|
|
188
|
+
}
|
|
189
|
+
const [wait, pollInterval] = this.validateWaitOptions(options);
|
|
190
|
+
this.logger.debug(sandboxRefreshProgress, `refresh - pollStatusAndAuth sandboxProcessObj, max wait time of ${wait.minutes} minutes`);
|
|
191
|
+
return this.pollStatusAndAuth({
|
|
192
|
+
sandboxProcessObj: sandboxRefreshProgress,
|
|
193
|
+
wait,
|
|
194
|
+
pollInterval,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
144
197
|
/**
|
|
145
198
|
*
|
|
146
199
|
* @param sandboxReq SandboxRequest options to create the sandbox with
|
|
@@ -154,10 +207,10 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
154
207
|
return this.createSandbox(sandboxReq, options);
|
|
155
208
|
}
|
|
156
209
|
/**
|
|
157
|
-
* Resume a sandbox
|
|
210
|
+
* Resume a sandbox create or refresh from a production org.
|
|
158
211
|
* `this` needs to be a production org with sandbox licenses available.
|
|
159
212
|
*
|
|
160
|
-
* @param resumeSandboxRequest SandboxRequest options to create the sandbox with
|
|
213
|
+
* @param resumeSandboxRequest SandboxRequest options to create/refresh the sandbox with
|
|
161
214
|
* @param options Wait: The amount of time to wait (default: 0 minutes) before timing out,
|
|
162
215
|
* Interval: The time interval (default: 30 seconds) between polling
|
|
163
216
|
*/
|
|
@@ -861,7 +914,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
861
914
|
async queryLatestSandboxProcessBySandboxName(sandboxNameIn) {
|
|
862
915
|
const { tooling } = this.getConnection();
|
|
863
916
|
this.logger.debug(`QueryLatestSandboxProcessBySandboxName called with SandboxName: ${sandboxNameIn}`);
|
|
864
|
-
const queryStr = `SELECT
|
|
917
|
+
const queryStr = `SELECT ${sandboxProcessFields.join(',')} FROM SandboxProcess WHERE SandboxName='${sandboxNameIn}' AND Status != 'D' ORDER BY CreatedDate DESC LIMIT 1`;
|
|
865
918
|
const queryResult = await tooling.query(queryStr);
|
|
866
919
|
this.logger.debug(queryResult, 'Return from calling queryToolingApi');
|
|
867
920
|
if (queryResult?.records?.length === 1) {
|
|
@@ -1071,6 +1124,17 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
1071
1124
|
oauth2Options.privateKey = productionAuthFields.privateKey;
|
|
1072
1125
|
oauth2Options.clientId = productionAuthFields.clientId;
|
|
1073
1126
|
}
|
|
1127
|
+
// Before creating the AuthInfo, delete any existing auth files for the sandbox.
|
|
1128
|
+
// This is common when refreshing sandboxes, and will cause AuthInfo to throw
|
|
1129
|
+
// because it doesn't want to overwrite existing auth files.
|
|
1130
|
+
const stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
|
|
1131
|
+
try {
|
|
1132
|
+
await stateAggregator.orgs.read(sandboxRes.authUserName);
|
|
1133
|
+
await stateAggregator.orgs.remove(sandboxRes.authUserName);
|
|
1134
|
+
}
|
|
1135
|
+
catch (e) {
|
|
1136
|
+
// ignore since this is only for deleting existing auth files.
|
|
1137
|
+
}
|
|
1074
1138
|
const authInfo = await authInfo_1.AuthInfo.create({
|
|
1075
1139
|
username: sandboxRes.authUserName,
|
|
1076
1140
|
oauth2Options,
|
|
@@ -1081,8 +1145,11 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
1081
1145
|
productionAuthFieldsUsername: productionAuthFields.username,
|
|
1082
1146
|
...oauth2Options,
|
|
1083
1147
|
}, 'Creating AuthInfo for sandbox');
|
|
1084
|
-
// save auth info for
|
|
1085
|
-
await authInfo.save(
|
|
1148
|
+
// save auth info for sandbox
|
|
1149
|
+
await authInfo.save({
|
|
1150
|
+
isScratch: false,
|
|
1151
|
+
isSandbox: true,
|
|
1152
|
+
});
|
|
1086
1153
|
const sandboxOrgId = authInfo.getFields().orgId;
|
|
1087
1154
|
if (!sandboxOrgId) {
|
|
1088
1155
|
throw messages.createError('AuthInfoOrgIdUndefined');
|
|
@@ -1157,7 +1224,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
1157
1224
|
* @private
|
|
1158
1225
|
*/
|
|
1159
1226
|
async querySandboxProcess(where) {
|
|
1160
|
-
const soql = `SELECT
|
|
1227
|
+
const soql = `SELECT ${sandboxProcessFields.join(',')} FROM SandboxProcess WHERE ${where} ORDER BY CreatedDate DESC`;
|
|
1161
1228
|
const result = (await this.connection.tooling.query(soql)).records.filter((item) => !item.Status.startsWith('Del'));
|
|
1162
1229
|
if (result.length === 0) {
|
|
1163
1230
|
throw new sfError_1.SfError(`No record found for ${soql}`, connection_1.SingleRecordQueryErrors.NoRecords);
|
|
@@ -1204,15 +1271,15 @@ class Org extends kit_1.AsyncOptionalCreatable {
|
|
|
1204
1271
|
return result;
|
|
1205
1272
|
}
|
|
1206
1273
|
catch (err) {
|
|
1207
|
-
const error = err;
|
|
1274
|
+
const error = err instanceof Error ? err : sfError_1.SfError.wrap((0, ts_types_1.isString)(err) ? err : 'unknown');
|
|
1208
1275
|
// There are cases where the endDate is set before the sandbox has actually completed.
|
|
1209
1276
|
// In that case, the sandboxAuth call will throw a specific exception.
|
|
1210
1277
|
if (error?.name === 'INVALID_STATUS') {
|
|
1211
|
-
this.logger.debug('Error while authenticating the user', error
|
|
1278
|
+
this.logger.debug('Error while authenticating the user:', error.message);
|
|
1212
1279
|
}
|
|
1213
1280
|
else {
|
|
1214
|
-
// If it fails for any unexpected reason,
|
|
1215
|
-
throw
|
|
1281
|
+
// If it fails for any unexpected reason, rethrow
|
|
1282
|
+
throw error;
|
|
1216
1283
|
}
|
|
1217
1284
|
}
|
|
1218
1285
|
}
|
package/messages/encryption.md
CHANGED
|
@@ -83,3 +83,16 @@ Command failed with response:
|
|
|
83
83
|
# macKeychainOutOfSync
|
|
84
84
|
|
|
85
85
|
We’ve encountered an error with the Mac keychain being out of sync with your `sfdx` credentials. To fix the problem, sync your credentials by authenticating into your org again using the auth commands.
|
|
86
|
+
|
|
87
|
+
# v1CryptoWithV2KeyWarning
|
|
88
|
+
|
|
89
|
+
The SF_CRYPTO_V2 environment variable was set to "false" but a v2 crypto key was detected. v1 crypto can only be used with a v1 key. Unset the SF_CRYPTO_V2 environment variable.
|
|
90
|
+
|
|
91
|
+
# v2CryptoWithV1KeyWarning
|
|
92
|
+
|
|
93
|
+
SF_CRYPTO_V2 was set to "true" but a v1 crypto key was detected. v2 crypto can only be used with a v2 key. To generate a v2 key:
|
|
94
|
+
|
|
95
|
+
1. Logout of all orgs: `sf org logout --all`
|
|
96
|
+
2. Delete the sfdx keychain entry (account: local, service: sfdx). If `SF_USE_GENERIC_UNIX_KEYCHAIN=true` env var is set, you can delete the `key.json` file.
|
|
97
|
+
3. Set `SF_CRYPTO_V2=true` env var.
|
|
98
|
+
4. Re-Authenticate with your orgs using the CLI org login commands.
|
package/messages/org.md
CHANGED
|
@@ -34,6 +34,10 @@ We can't find a SandboxProcess for the sandbox %s.
|
|
|
34
34
|
|
|
35
35
|
The sandbox org creation failed with a result of %s.
|
|
36
36
|
|
|
37
|
+
# sandboxInfoRefreshFailed
|
|
38
|
+
|
|
39
|
+
The sandbox org refresh failed with a result of %s.
|
|
40
|
+
|
|
37
41
|
# missingAuthUsername
|
|
38
42
|
|
|
39
43
|
The sandbox %s does not have an authorized username.
|