@salesforce/core 3.31.4 → 3.31.7
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 +11 -11
- package/README.md +222 -222
- package/lib/config/aliasesConfig.d.ts +12 -12
- package/lib/config/aliasesConfig.js +27 -27
- package/lib/config/authInfoConfig.d.ts +19 -19
- package/lib/config/authInfoConfig.js +34 -34
- package/lib/config/config.d.ts +311 -311
- package/lib/config/config.js +574 -574
- package/lib/config/configAggregator.d.ts +232 -232
- package/lib/config/configAggregator.js +379 -379
- package/lib/config/configFile.d.ts +199 -199
- package/lib/config/configFile.js +340 -340
- package/lib/config/configGroup.d.ts +141 -141
- package/lib/config/configGroup.js +224 -224
- package/lib/config/configStore.d.ts +241 -241
- package/lib/config/configStore.js +352 -352
- package/lib/config/envVars.d.ts +101 -101
- package/lib/config/envVars.js +456 -456
- package/lib/config/orgUsersConfig.d.ts +31 -31
- package/lib/config/orgUsersConfig.js +41 -41
- package/lib/config/sandboxOrgConfig.d.ts +37 -37
- package/lib/config/sandboxOrgConfig.js +50 -50
- package/lib/config/sandboxProcessCache.d.ts +16 -16
- package/lib/config/sandboxProcessCache.js +37 -37
- package/lib/config/tokensConfig.d.ts +10 -10
- package/lib/config/tokensConfig.js +28 -28
- package/lib/config/ttlConfig.d.ts +34 -34
- package/lib/config/ttlConfig.js +54 -54
- package/lib/crypto/crypto.d.ts +54 -54
- package/lib/crypto/crypto.js +220 -220
- package/lib/crypto/keyChain.d.ts +8 -8
- package/lib/crypto/keyChain.js +61 -61
- package/lib/crypto/keyChainImpl.d.ts +116 -116
- package/lib/crypto/keyChainImpl.js +486 -486
- package/lib/crypto/secureBuffer.d.ts +46 -46
- package/lib/crypto/secureBuffer.js +82 -82
- package/lib/deviceOauthService.d.ts +71 -71
- package/lib/deviceOauthService.js +191 -191
- package/lib/exported.d.ts +38 -38
- package/lib/exported.js +118 -118
- package/lib/global.d.ts +70 -70
- package/lib/global.js +109 -109
- package/lib/lifecycleEvents.d.ts +93 -93
- package/lib/lifecycleEvents.js +188 -188
- package/lib/logger.d.ts +381 -381
- package/lib/logger.js +734 -734
- package/lib/messages.d.ts +291 -291
- package/lib/messages.js +543 -543
- package/lib/org/authInfo.d.ts +344 -344
- package/lib/org/authInfo.js +892 -892
- package/lib/org/authRemover.d.ts +88 -88
- package/lib/org/authRemover.js +182 -182
- package/lib/org/connection.d.ts +197 -197
- package/lib/org/connection.js +395 -395
- package/lib/org/index.d.ts +6 -6
- package/lib/org/index.js +28 -28
- package/lib/org/org.d.ts +558 -558
- package/lib/org/org.js +1267 -1267
- package/lib/org/orgConfigProperties.d.ts +69 -69
- package/lib/org/orgConfigProperties.js +136 -136
- package/lib/org/permissionSetAssignment.d.ts +35 -35
- package/lib/org/permissionSetAssignment.js +125 -125
- package/lib/org/scratchOrgCache.d.ts +20 -20
- package/lib/org/scratchOrgCache.js +32 -32
- package/lib/org/scratchOrgCreate.d.ts +54 -54
- package/lib/org/scratchOrgCreate.js +216 -216
- package/lib/org/scratchOrgErrorCodes.d.ts +10 -10
- package/lib/org/scratchOrgErrorCodes.js +88 -88
- package/lib/org/scratchOrgFeatureDeprecation.d.ts +26 -26
- package/lib/org/scratchOrgFeatureDeprecation.js +109 -109
- package/lib/org/scratchOrgInfoApi.d.ts +68 -68
- package/lib/org/scratchOrgInfoApi.js +413 -413
- package/lib/org/scratchOrgInfoGenerator.d.ts +64 -64
- package/lib/org/scratchOrgInfoGenerator.js +241 -241
- package/lib/org/scratchOrgLifecycleEvents.d.ts +10 -10
- package/lib/org/scratchOrgLifecycleEvents.js +40 -40
- package/lib/org/scratchOrgSettingsGenerator.d.ts +78 -78
- package/lib/org/scratchOrgSettingsGenerator.js +276 -276
- package/lib/org/scratchOrgTypes.d.ts +43 -43
- package/lib/org/scratchOrgTypes.js +8 -8
- package/lib/org/user.d.ts +187 -187
- package/lib/org/user.js +448 -448
- package/lib/schema/printer.d.ts +79 -79
- package/lib/schema/printer.js +260 -260
- package/lib/schema/validator.d.ts +70 -70
- package/lib/schema/validator.js +169 -169
- package/lib/sfError.d.ts +73 -73
- package/lib/sfError.js +136 -136
- package/lib/sfProject.d.ts +357 -357
- package/lib/sfProject.js +671 -671
- package/lib/stateAggregator/accessors/aliasAccessor.d.ts +98 -98
- package/lib/stateAggregator/accessors/aliasAccessor.js +145 -145
- package/lib/stateAggregator/accessors/orgAccessor.d.ts +101 -101
- package/lib/stateAggregator/accessors/orgAccessor.js +240 -240
- package/lib/stateAggregator/accessors/sandboxAccessor.d.ts +8 -8
- package/lib/stateAggregator/accessors/sandboxAccessor.js +27 -27
- package/lib/stateAggregator/accessors/tokenAccessor.d.ts +63 -63
- package/lib/stateAggregator/accessors/tokenAccessor.js +79 -79
- package/lib/stateAggregator/index.d.ts +4 -4
- package/lib/stateAggregator/index.js +26 -26
- package/lib/stateAggregator/stateAggregator.d.ts +25 -25
- package/lib/stateAggregator/stateAggregator.js +45 -45
- package/lib/status/myDomainResolver.d.ts +66 -66
- package/lib/status/myDomainResolver.js +124 -124
- package/lib/status/pollingClient.d.ts +85 -85
- package/lib/status/pollingClient.js +115 -115
- package/lib/status/streamingClient.d.ts +244 -244
- package/lib/status/streamingClient.js +436 -436
- package/lib/status/types.d.ts +89 -89
- package/lib/status/types.js +17 -17
- package/lib/testSetup.d.ts +553 -553
- package/lib/testSetup.js +871 -871
- package/lib/util/cache.d.ts +11 -11
- package/lib/util/cache.js +69 -69
- package/lib/util/checkLightningDomain.d.ts +1 -1
- package/lib/util/checkLightningDomain.js +28 -28
- package/lib/util/directoryWriter.d.ts +12 -12
- package/lib/util/directoryWriter.js +53 -53
- package/lib/util/getJwtAudienceUrl.d.ts +4 -4
- package/lib/util/getJwtAudienceUrl.js +18 -18
- package/lib/util/internal.d.ts +58 -58
- package/lib/util/internal.js +118 -118
- package/lib/util/jsonXmlTools.d.ts +14 -14
- package/lib/util/jsonXmlTools.js +38 -38
- package/lib/util/mapKeys.d.ts +14 -14
- package/lib/util/mapKeys.js +51 -51
- package/lib/util/sfdc.d.ts +52 -52
- package/lib/util/sfdc.js +85 -85
- package/lib/util/sfdcUrl.d.ts +72 -72
- package/lib/util/sfdcUrl.js +215 -215
- package/lib/util/structuredWriter.d.ts +9 -9
- package/lib/util/structuredWriter.js +2 -2
- package/lib/util/zipWriter.d.ts +16 -16
- package/lib/util/zipWriter.js +67 -67
- package/lib/webOAuthServer.d.ts +156 -156
- package/lib/webOAuthServer.js +388 -388
- package/messages/auth.md +37 -37
- package/messages/config.md +156 -156
- package/messages/connection.md +30 -30
- package/messages/core.json +20 -20
- package/messages/core.md +67 -67
- package/messages/encryption.md +85 -85
- package/messages/envVars.md +303 -303
- package/messages/org.md +63 -63
- package/messages/permissionSetAssignment.md +31 -31
- package/messages/scratchOrgCreate.md +23 -23
- package/messages/scratchOrgErrorCodes.md +115 -115
- package/messages/scratchOrgFeatureDeprecation.md +11 -11
- package/messages/scratchOrgInfoApi.md +15 -15
- package/messages/scratchOrgInfoGenerator.md +23 -23
- package/messages/streaming.md +23 -23
- package/messages/user.md +35 -35
- package/package.json +97 -97
package/lib/org/user.js
CHANGED
|
@@ -1,449 +1,449 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* Copyright (c) 2020, salesforce.com, inc.
|
|
4
|
-
* All rights reserved.
|
|
5
|
-
* Licensed under the BSD 3-Clause license.
|
|
6
|
-
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
7
|
-
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.User = exports.DefaultUserFields = exports.REQUIRED_FIELDS = void 0;
|
|
10
|
-
const os_1 = require("os");
|
|
11
|
-
const kit_1 = require("@salesforce/kit");
|
|
12
|
-
const ts_types_1 = require("@salesforce/ts-types");
|
|
13
|
-
const http_api_1 = require("jsforce/lib/http-api");
|
|
14
|
-
const logger_1 = require("../logger");
|
|
15
|
-
const messages_1 = require("../messages");
|
|
16
|
-
const secureBuffer_1 = require("../crypto/secureBuffer");
|
|
17
|
-
const sfError_1 = require("../sfError");
|
|
18
|
-
const sfdc_1 = require("../util/sfdc");
|
|
19
|
-
const connection_1 = require("./connection");
|
|
20
|
-
const permissionSetAssignment_1 = require("./permissionSetAssignment");
|
|
21
|
-
const authInfo_1 = require("./authInfo");
|
|
22
|
-
const rand = (len) => Math.floor(Math.random() * len.length);
|
|
23
|
-
const CHARACTERS = {
|
|
24
|
-
LOWER: 'abcdefghijklmnopqrstuvwxyz',
|
|
25
|
-
UPPER: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
|
26
|
-
NUMBERS: '1234567890',
|
|
27
|
-
SYMBOLS: ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '[', ']', '|', '-'],
|
|
28
|
-
};
|
|
29
|
-
const PASSWORD_COMPLEXITY = {
|
|
30
|
-
'0': { LOWER: true },
|
|
31
|
-
'1': { LOWER: true, NUMBERS: true },
|
|
32
|
-
'2': { LOWER: true, SYMBOLS: true },
|
|
33
|
-
'3': { LOWER: true, UPPER: true, NUMBERS: true },
|
|
34
|
-
'4': { LOWER: true, NUMBERS: true, SYMBOLS: true },
|
|
35
|
-
'5': { LOWER: true, UPPER: true, NUMBERS: true, SYMBOLS: true },
|
|
36
|
-
};
|
|
37
|
-
const scimEndpoint = '/services/scim/v1/Users';
|
|
38
|
-
const scimHeaders = { 'auto-approve-user': 'true' };
|
|
39
|
-
messages_1.Messages.importMessagesDirectory(__dirname);
|
|
40
|
-
const messages = messages_1.Messages.load('@salesforce/core', 'user', [
|
|
41
|
-
'invalidHttpResponseCreatingUser',
|
|
42
|
-
'userQueryFailed',
|
|
43
|
-
'missingId',
|
|
44
|
-
'permsetNamesAreRequired',
|
|
45
|
-
'missingFields',
|
|
46
|
-
'lengthOutOfBound',
|
|
47
|
-
'complexityOutOfBound',
|
|
48
|
-
]);
|
|
49
|
-
/**
|
|
50
|
-
* A Map of Required Salesforce User fields.
|
|
51
|
-
*/
|
|
52
|
-
exports.REQUIRED_FIELDS = {
|
|
53
|
-
id: 'id',
|
|
54
|
-
username: 'username',
|
|
55
|
-
lastName: 'lastName',
|
|
56
|
-
alias: 'alias',
|
|
57
|
-
timeZoneSidKey: 'timeZoneSidKey',
|
|
58
|
-
localeSidKey: 'localeSidKey',
|
|
59
|
-
emailEncodingKey: 'emailEncodingKey',
|
|
60
|
-
profileId: 'profileId',
|
|
61
|
-
languageLocaleKey: 'languageLocaleKey',
|
|
62
|
-
email: 'email',
|
|
63
|
-
};
|
|
64
|
-
/**
|
|
65
|
-
* Helper method to lookup UserFields.
|
|
66
|
-
*
|
|
67
|
-
* @param logger
|
|
68
|
-
* @param username The username.
|
|
69
|
-
*/
|
|
70
|
-
async function retrieveUserFields(logger, username) {
|
|
71
|
-
const connection = await connection_1.Connection.create({
|
|
72
|
-
authInfo: await authInfo_1.AuthInfo.create({ username }),
|
|
73
|
-
});
|
|
74
|
-
if (sfdc_1.sfdc.matchesAccessToken(username)) {
|
|
75
|
-
logger.debug('received an accessToken for the username. Converting...');
|
|
76
|
-
username = (await connection.identity()).username;
|
|
77
|
-
logger.debug(`accessToken converted to ${username}`);
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
logger.debug('not a accessToken');
|
|
81
|
-
}
|
|
82
|
-
const fromFields = Object.keys(exports.REQUIRED_FIELDS).map(kit_1.upperFirst);
|
|
83
|
-
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
84
|
-
const requiredFieldsFromAdminQuery = `SELECT ${fromFields} FROM User WHERE Username='${username}'`;
|
|
85
|
-
const result = await connection.query(requiredFieldsFromAdminQuery);
|
|
86
|
-
logger.debug('Successfully retrieved the admin user for this org.');
|
|
87
|
-
if (result.totalSize === 1) {
|
|
88
|
-
const results = (0, kit_1.mapKeys)(result.records[0], (value, key) => (0, kit_1.lowerFirst)(key));
|
|
89
|
-
const fields = {
|
|
90
|
-
id: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.id]),
|
|
91
|
-
username,
|
|
92
|
-
alias: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.alias]),
|
|
93
|
-
email: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.email]),
|
|
94
|
-
emailEncodingKey: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.emailEncodingKey]),
|
|
95
|
-
languageLocaleKey: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.languageLocaleKey]),
|
|
96
|
-
localeSidKey: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.localeSidKey]),
|
|
97
|
-
profileId: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.profileId]),
|
|
98
|
-
lastName: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.lastName]),
|
|
99
|
-
timeZoneSidKey: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.timeZoneSidKey]),
|
|
100
|
-
};
|
|
101
|
-
return fields;
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
throw messages.createError('userQueryFailed', [username]);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Gets the profile id associated with a profile name.
|
|
109
|
-
*
|
|
110
|
-
* @param name The name of the profile.
|
|
111
|
-
* @param connection The connection for the query.
|
|
112
|
-
*/
|
|
113
|
-
async function retrieveProfileId(name, connection) {
|
|
114
|
-
if (!sfdc_1.sfdc.validateSalesforceId(name)) {
|
|
115
|
-
const profileQuery = `SELECT Id FROM Profile WHERE name='${name}'`;
|
|
116
|
-
const result = await connection.query(profileQuery);
|
|
117
|
-
if (result.records.length > 0) {
|
|
118
|
-
return result.records[0].Id;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return name;
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Provides a default set of fields values that can be used to create a user. This is handy for
|
|
125
|
-
* software development purposes.
|
|
126
|
-
*
|
|
127
|
-
* ```
|
|
128
|
-
* const connection: Connection = await Connection.create({
|
|
129
|
-
* authInfo: await AuthInfo.create({ username: 'user@example.com' })
|
|
130
|
-
* });
|
|
131
|
-
* const org: Org = await Org.create({ connection });
|
|
132
|
-
* const options: DefaultUserFields.Options = {
|
|
133
|
-
* templateUser: org.getUsername()
|
|
134
|
-
* };
|
|
135
|
-
* const fields = (await DefaultUserFields.create(options)).getFields();
|
|
136
|
-
* ```
|
|
137
|
-
*/
|
|
138
|
-
class DefaultUserFields extends kit_1.AsyncCreatable {
|
|
139
|
-
/**
|
|
140
|
-
* @ignore
|
|
141
|
-
*/
|
|
142
|
-
constructor(options) {
|
|
143
|
-
super(options);
|
|
144
|
-
this.options = options || { templateUser: '' };
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Get user fields.
|
|
148
|
-
*/
|
|
149
|
-
getFields() {
|
|
150
|
-
return this.userFields;
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Initialize asynchronous components.
|
|
154
|
-
*/
|
|
155
|
-
async init() {
|
|
156
|
-
this.logger = await logger_1.Logger.child('DefaultUserFields');
|
|
157
|
-
this.userFields = await retrieveUserFields(this.logger, this.options.templateUser);
|
|
158
|
-
this.userFields.profileId = await retrieveProfileId('Standard User', await connection_1.Connection.create({
|
|
159
|
-
authInfo: await authInfo_1.AuthInfo.create({ username: this.options.templateUser }),
|
|
160
|
-
}));
|
|
161
|
-
this.logger.debug(`Standard User profileId: ${this.userFields.profileId}`);
|
|
162
|
-
if (this.options.newUserName) {
|
|
163
|
-
this.userFields.username = this.options.newUserName;
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
this.userFields.username = `${Date.now()}_${this.userFields.username}`;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
exports.DefaultUserFields = DefaultUserFields;
|
|
171
|
-
/**
|
|
172
|
-
* A class for creating a User, generating a password for a user, and assigning a user to one or more permission sets.
|
|
173
|
-
* See methods for examples.
|
|
174
|
-
*/
|
|
175
|
-
class User extends kit_1.AsyncCreatable {
|
|
176
|
-
/**
|
|
177
|
-
* @ignore
|
|
178
|
-
*/
|
|
179
|
-
constructor(options) {
|
|
180
|
-
super(options);
|
|
181
|
-
this.org = options.org;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Generate default password for a user. Returns An encrypted buffer containing a utf8 encoded password.
|
|
185
|
-
*/
|
|
186
|
-
static generatePasswordUtf8(passwordCondition = { length: 13, complexity: 5 }) {
|
|
187
|
-
if (!PASSWORD_COMPLEXITY[passwordCondition.complexity]) {
|
|
188
|
-
const msg = messages.getMessage('complexityOutOfBound');
|
|
189
|
-
throw new sfError_1.SfError(msg, 'complexityOutOfBound');
|
|
190
|
-
}
|
|
191
|
-
if (passwordCondition.length < 8 || passwordCondition.length > 1000) {
|
|
192
|
-
const msg = messages.getMessage('lengthOutOfBound');
|
|
193
|
-
throw new sfError_1.SfError(msg, 'lengthOutOfBound');
|
|
194
|
-
}
|
|
195
|
-
let password = [];
|
|
196
|
-
['SYMBOLS', 'NUMBERS', 'UPPER', 'LOWER'].forEach((charSet) => {
|
|
197
|
-
if (PASSWORD_COMPLEXITY[passwordCondition.complexity][charSet]) {
|
|
198
|
-
password.push(CHARACTERS[charSet][rand(CHARACTERS[charSet])]);
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
// Concatinating remaining length randomly with all lower characters
|
|
202
|
-
password = password.concat(Array(Math.max(passwordCondition.length - password.length, 0))
|
|
203
|
-
.fill('0')
|
|
204
|
-
.map(() => CHARACTERS['LOWER'][rand(CHARACTERS['LOWER'])]));
|
|
205
|
-
password = password.sort(() => Math.random() - 0.5);
|
|
206
|
-
const secureBuffer = new secureBuffer_1.SecureBuffer();
|
|
207
|
-
secureBuffer.consume(Buffer.from(password.join(''), 'utf8'));
|
|
208
|
-
return secureBuffer;
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Initialize a new instance of a user and return it.
|
|
212
|
-
*/
|
|
213
|
-
async init() {
|
|
214
|
-
this.logger = await logger_1.Logger.child('User');
|
|
215
|
-
await this.org.refreshAuth();
|
|
216
|
-
this.logger.debug('Auth refresh ok');
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Assigns a password to a user. For a user to have the ability to assign their own password, the org needs the
|
|
220
|
-
* following org feature: EnableSetPasswordInApi.
|
|
221
|
-
*
|
|
222
|
-
* @param info The AuthInfo object for user to assign the password to.
|
|
223
|
-
* @param password [throwWhenRemoveFails = User.generatePasswordUtf8()] A SecureBuffer containing the new password.
|
|
224
|
-
*/
|
|
225
|
-
async assignPassword(info, password = User.generatePasswordUtf8()) {
|
|
226
|
-
this.logger.debug(`Attempting to set password for userId: ${info.getFields().userId} username: ${info.getFields().username}`);
|
|
227
|
-
const userConnection = await connection_1.Connection.create({ authInfo: info });
|
|
228
|
-
return new Promise((resolve, reject) => {
|
|
229
|
-
// no promises in async method
|
|
230
|
-
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
231
|
-
password.value(async (buffer) => {
|
|
232
|
-
try {
|
|
233
|
-
const soap = userConnection.soap;
|
|
234
|
-
await soap.setPassword((0, ts_types_1.ensureString)(info.getFields().userId), buffer.toString('utf8'));
|
|
235
|
-
this.logger.debug(`Set password for userId: ${info.getFields().userId}`);
|
|
236
|
-
resolve();
|
|
237
|
-
}
|
|
238
|
-
catch (e) {
|
|
239
|
-
reject(e);
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* Methods to assign one or more permission set names to a user.
|
|
246
|
-
*
|
|
247
|
-
* @param id The Salesforce id of the user to assign the permission set to.
|
|
248
|
-
* @param permsetNames An array of permission set names.
|
|
249
|
-
*
|
|
250
|
-
* ```
|
|
251
|
-
* const username = 'user@example.com';
|
|
252
|
-
* const connection: Connection = await Connection.create({
|
|
253
|
-
* authInfo: await AuthInfo.create({ username })
|
|
254
|
-
* });
|
|
255
|
-
* const org = await Org.create({ connection });
|
|
256
|
-
* const user: User = await User.create({ org });
|
|
257
|
-
* const fields: UserFields = await user.retrieve(username);
|
|
258
|
-
* await user.assignPermissionSets(fields.id, ['sfdx', 'approver']);
|
|
259
|
-
* ```
|
|
260
|
-
*/
|
|
261
|
-
async assignPermissionSets(id, permsetNames) {
|
|
262
|
-
if (!id) {
|
|
263
|
-
throw messages.createError('missingId');
|
|
264
|
-
}
|
|
265
|
-
if (!permsetNames) {
|
|
266
|
-
throw messages.createError('permsetNamesAreRequired');
|
|
267
|
-
}
|
|
268
|
-
const assignments = await permissionSetAssignment_1.PermissionSetAssignment.init(this.org);
|
|
269
|
-
await Promise.all(permsetNames.map((permsetName) => assignments.create(id, permsetName)));
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* Method for creating a new User.
|
|
273
|
-
*
|
|
274
|
-
* By default scratch orgs only allow creating 2 additional users. Work with Salesforce Customer Service to increase
|
|
275
|
-
* user limits.
|
|
276
|
-
*
|
|
277
|
-
* The Org Preferences required to increase the number of users are:
|
|
278
|
-
* Standard User Licenses
|
|
279
|
-
* Salesforce CRM Content User
|
|
280
|
-
*
|
|
281
|
-
* @param fields The required fields for creating a user.
|
|
282
|
-
*
|
|
283
|
-
* ```
|
|
284
|
-
* const connection: Connection = await Connection.create({
|
|
285
|
-
* authInfo: await AuthInfo.create({ username: 'user@example.com' })
|
|
286
|
-
* });
|
|
287
|
-
* const org = await Org.create({ connection });
|
|
288
|
-
*
|
|
289
|
-
* const defaultUserFields = await DefaultUserFields.create({ templateUser: 'devhub_user@example.com' });
|
|
290
|
-
* const user: User = await User.create({ org });
|
|
291
|
-
* const info: AuthInfo = await user.createUser(defaultUserFields.getFields());
|
|
292
|
-
* ```
|
|
293
|
-
*/
|
|
294
|
-
async createUser(fields) {
|
|
295
|
-
// Create a user and get a refresh token
|
|
296
|
-
const refreshTokenSecret = await this.createUserInternal(fields);
|
|
297
|
-
// Create the initial auth info
|
|
298
|
-
const authInfo = await authInfo_1.AuthInfo.create({ username: this.org.getUsername() });
|
|
299
|
-
const adminUserAuthFields = authInfo.getFields(true);
|
|
300
|
-
// Setup oauth options for the new user
|
|
301
|
-
const oauthOptions = {
|
|
302
|
-
// Communities users require the instance for auth
|
|
303
|
-
loginUrl: adminUserAuthFields.instanceUrl ?? adminUserAuthFields.loginUrl,
|
|
304
|
-
refreshToken: refreshTokenSecret.buffer.value((buffer) => buffer.toString('utf8')),
|
|
305
|
-
clientId: adminUserAuthFields.clientId,
|
|
306
|
-
clientSecret: adminUserAuthFields.clientSecret,
|
|
307
|
-
privateKey: adminUserAuthFields.privateKey,
|
|
308
|
-
};
|
|
309
|
-
// Create an auth info object for the new user
|
|
310
|
-
const newUserAuthInfo = await authInfo_1.AuthInfo.create({
|
|
311
|
-
username: fields.username,
|
|
312
|
-
oauth2Options: oauthOptions,
|
|
313
|
-
});
|
|
314
|
-
// Update the auth info object with created user id.
|
|
315
|
-
const newUserAuthFields = newUserAuthInfo.getFields();
|
|
316
|
-
newUserAuthFields.userId = refreshTokenSecret.userId;
|
|
317
|
-
// Make sure we can connect and if so save the auth info.
|
|
318
|
-
await this.describeUserAndSave(newUserAuthInfo);
|
|
319
|
-
// Let the org know there is a new user. See $HOME/.sfdx/[orgid].json for the mapping.
|
|
320
|
-
await this.org.addUsername(newUserAuthInfo);
|
|
321
|
-
return newUserAuthInfo;
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Method to retrieve the UserFields for a user.
|
|
325
|
-
*
|
|
326
|
-
* @param username The username of the user.
|
|
327
|
-
*
|
|
328
|
-
* ```
|
|
329
|
-
* const username = 'boris@thecat.com';
|
|
330
|
-
* const connection: Connection = await Connection.create({
|
|
331
|
-
* authInfo: await AuthInfo.create({ username })
|
|
332
|
-
* });
|
|
333
|
-
* const org = await Org.create({ connection });
|
|
334
|
-
* const user: User = await User.create({ org });
|
|
335
|
-
* const fields: UserFields = await user.retrieve(username);
|
|
336
|
-
* ```
|
|
337
|
-
*/
|
|
338
|
-
async retrieve(username) {
|
|
339
|
-
return retrieveUserFields(this.logger, username);
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* Helper method that verifies the server's User object is available and if so allows persisting the Auth information.
|
|
343
|
-
*
|
|
344
|
-
* @param newUserAuthInfo The AuthInfo for the new user.
|
|
345
|
-
*/
|
|
346
|
-
async describeUserAndSave(newUserAuthInfo) {
|
|
347
|
-
const connection = await connection_1.Connection.create({ authInfo: newUserAuthInfo });
|
|
348
|
-
this.logger.debug(`Created connection for user: ${newUserAuthInfo.getUsername()}`);
|
|
349
|
-
const userDescribe = await connection.describe('User');
|
|
350
|
-
if (userDescribe?.fields) {
|
|
351
|
-
await newUserAuthInfo.save();
|
|
352
|
-
return newUserAuthInfo;
|
|
353
|
-
}
|
|
354
|
-
else {
|
|
355
|
-
throw messages.createError('permsetNamesAreRequired');
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* Helper that makes a REST request to create the user, and update additional required fields.
|
|
360
|
-
*
|
|
361
|
-
* @param fields The configuration the new user should have.
|
|
362
|
-
*/
|
|
363
|
-
async createUserInternal(fields) {
|
|
364
|
-
if (!fields) {
|
|
365
|
-
throw messages.createError('missingFields');
|
|
366
|
-
}
|
|
367
|
-
const conn = this.org.getConnection();
|
|
368
|
-
const body = JSON.stringify({
|
|
369
|
-
username: fields.username,
|
|
370
|
-
emails: [fields.email],
|
|
371
|
-
name: {
|
|
372
|
-
familyName: fields.lastName,
|
|
373
|
-
},
|
|
374
|
-
nickName: fields.username.substring(0, 40),
|
|
375
|
-
entitlements: [
|
|
376
|
-
{
|
|
377
|
-
value: fields.profileId,
|
|
378
|
-
},
|
|
379
|
-
],
|
|
380
|
-
});
|
|
381
|
-
this.logger.debug(`user create request body: ${body}`);
|
|
382
|
-
const scimUrl = conn.normalizeUrl(scimEndpoint);
|
|
383
|
-
this.logger.debug(`scimUrl: ${scimUrl}`);
|
|
384
|
-
const info = {
|
|
385
|
-
method: 'POST',
|
|
386
|
-
url: scimUrl,
|
|
387
|
-
headers: scimHeaders,
|
|
388
|
-
body,
|
|
389
|
-
};
|
|
390
|
-
const response = await this.rawRequest(conn, info);
|
|
391
|
-
const responseBody = (0, kit_1.parseJsonMap)((0, ts_types_1.ensureString)(response['body']));
|
|
392
|
-
const statusCode = (0, ts_types_1.asNumber)(response.statusCode);
|
|
393
|
-
this.logger.debug(`user create response.statusCode: ${response.statusCode}`);
|
|
394
|
-
if (!(statusCode === 201 || statusCode === 200)) {
|
|
395
|
-
let message = messages.getMessage('invalidHttpResponseCreatingUser', [statusCode]);
|
|
396
|
-
if (responseBody) {
|
|
397
|
-
const errors = (0, ts_types_1.asJsonArray)(responseBody.Errors);
|
|
398
|
-
if (errors && errors.length > 0) {
|
|
399
|
-
message = `${message} causes:${os_1.EOL}`;
|
|
400
|
-
errors.forEach((singleMessage) => {
|
|
401
|
-
if (!(0, ts_types_1.isJsonMap)(singleMessage))
|
|
402
|
-
return;
|
|
403
|
-
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
404
|
-
message = `${message}${os_1.EOL}${singleMessage.description}`;
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
this.logger.debug(message);
|
|
409
|
-
throw new sfError_1.SfError(message, 'UserCreateHttpError');
|
|
410
|
-
}
|
|
411
|
-
fields.id = (0, ts_types_1.ensureString)(responseBody.id);
|
|
412
|
-
await this.updateRequiredUserFields(fields);
|
|
413
|
-
const buffer = new secureBuffer_1.SecureBuffer();
|
|
414
|
-
const headers = (0, ts_types_1.ensureJsonMap)(response.headers);
|
|
415
|
-
const autoApproveUser = (0, ts_types_1.ensureString)(headers['auto-approve-user']);
|
|
416
|
-
buffer.consume(Buffer.from(autoApproveUser));
|
|
417
|
-
return {
|
|
418
|
-
buffer,
|
|
419
|
-
userId: fields.id,
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
// eslint-disable-next-line class-methods-use-this
|
|
423
|
-
async rawRequest(conn, options) {
|
|
424
|
-
return new Promise((resolve, reject) => {
|
|
425
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
426
|
-
const httpApi = new http_api_1.HttpApi(conn, options);
|
|
427
|
-
httpApi.on('response', (response) => resolve(response));
|
|
428
|
-
httpApi.request(options).catch(reject);
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* Update the remaining required fields for the user.
|
|
433
|
-
*
|
|
434
|
-
* @param fields The fields for the user.
|
|
435
|
-
*/
|
|
436
|
-
async updateRequiredUserFields(fields) {
|
|
437
|
-
const leftOverRequiredFields = (0, kit_1.omit)(fields, [
|
|
438
|
-
exports.REQUIRED_FIELDS.username,
|
|
439
|
-
exports.REQUIRED_FIELDS.email,
|
|
440
|
-
exports.REQUIRED_FIELDS.lastName,
|
|
441
|
-
exports.REQUIRED_FIELDS.profileId,
|
|
442
|
-
]);
|
|
443
|
-
const object = (0, kit_1.mapKeys)(leftOverRequiredFields, (value, key) => (0, kit_1.upperFirst)(key));
|
|
444
|
-
await this.org.getConnection().sobject('User').update(object);
|
|
445
|
-
this.logger.debug(`Successfully Updated additional properties for user: ${fields.username}`);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
exports.User = User;
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2020, salesforce.com, inc.
|
|
4
|
+
* All rights reserved.
|
|
5
|
+
* Licensed under the BSD 3-Clause license.
|
|
6
|
+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.User = exports.DefaultUserFields = exports.REQUIRED_FIELDS = void 0;
|
|
10
|
+
const os_1 = require("os");
|
|
11
|
+
const kit_1 = require("@salesforce/kit");
|
|
12
|
+
const ts_types_1 = require("@salesforce/ts-types");
|
|
13
|
+
const http_api_1 = require("jsforce/lib/http-api");
|
|
14
|
+
const logger_1 = require("../logger");
|
|
15
|
+
const messages_1 = require("../messages");
|
|
16
|
+
const secureBuffer_1 = require("../crypto/secureBuffer");
|
|
17
|
+
const sfError_1 = require("../sfError");
|
|
18
|
+
const sfdc_1 = require("../util/sfdc");
|
|
19
|
+
const connection_1 = require("./connection");
|
|
20
|
+
const permissionSetAssignment_1 = require("./permissionSetAssignment");
|
|
21
|
+
const authInfo_1 = require("./authInfo");
|
|
22
|
+
const rand = (len) => Math.floor(Math.random() * len.length);
|
|
23
|
+
const CHARACTERS = {
|
|
24
|
+
LOWER: 'abcdefghijklmnopqrstuvwxyz',
|
|
25
|
+
UPPER: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
|
26
|
+
NUMBERS: '1234567890',
|
|
27
|
+
SYMBOLS: ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '[', ']', '|', '-'],
|
|
28
|
+
};
|
|
29
|
+
const PASSWORD_COMPLEXITY = {
|
|
30
|
+
'0': { LOWER: true },
|
|
31
|
+
'1': { LOWER: true, NUMBERS: true },
|
|
32
|
+
'2': { LOWER: true, SYMBOLS: true },
|
|
33
|
+
'3': { LOWER: true, UPPER: true, NUMBERS: true },
|
|
34
|
+
'4': { LOWER: true, NUMBERS: true, SYMBOLS: true },
|
|
35
|
+
'5': { LOWER: true, UPPER: true, NUMBERS: true, SYMBOLS: true },
|
|
36
|
+
};
|
|
37
|
+
const scimEndpoint = '/services/scim/v1/Users';
|
|
38
|
+
const scimHeaders = { 'auto-approve-user': 'true' };
|
|
39
|
+
messages_1.Messages.importMessagesDirectory(__dirname);
|
|
40
|
+
const messages = messages_1.Messages.load('@salesforce/core', 'user', [
|
|
41
|
+
'invalidHttpResponseCreatingUser',
|
|
42
|
+
'userQueryFailed',
|
|
43
|
+
'missingId',
|
|
44
|
+
'permsetNamesAreRequired',
|
|
45
|
+
'missingFields',
|
|
46
|
+
'lengthOutOfBound',
|
|
47
|
+
'complexityOutOfBound',
|
|
48
|
+
]);
|
|
49
|
+
/**
|
|
50
|
+
* A Map of Required Salesforce User fields.
|
|
51
|
+
*/
|
|
52
|
+
exports.REQUIRED_FIELDS = {
|
|
53
|
+
id: 'id',
|
|
54
|
+
username: 'username',
|
|
55
|
+
lastName: 'lastName',
|
|
56
|
+
alias: 'alias',
|
|
57
|
+
timeZoneSidKey: 'timeZoneSidKey',
|
|
58
|
+
localeSidKey: 'localeSidKey',
|
|
59
|
+
emailEncodingKey: 'emailEncodingKey',
|
|
60
|
+
profileId: 'profileId',
|
|
61
|
+
languageLocaleKey: 'languageLocaleKey',
|
|
62
|
+
email: 'email',
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Helper method to lookup UserFields.
|
|
66
|
+
*
|
|
67
|
+
* @param logger
|
|
68
|
+
* @param username The username.
|
|
69
|
+
*/
|
|
70
|
+
async function retrieveUserFields(logger, username) {
|
|
71
|
+
const connection = await connection_1.Connection.create({
|
|
72
|
+
authInfo: await authInfo_1.AuthInfo.create({ username }),
|
|
73
|
+
});
|
|
74
|
+
if (sfdc_1.sfdc.matchesAccessToken(username)) {
|
|
75
|
+
logger.debug('received an accessToken for the username. Converting...');
|
|
76
|
+
username = (await connection.identity()).username;
|
|
77
|
+
logger.debug(`accessToken converted to ${username}`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
logger.debug('not a accessToken');
|
|
81
|
+
}
|
|
82
|
+
const fromFields = Object.keys(exports.REQUIRED_FIELDS).map(kit_1.upperFirst);
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
84
|
+
const requiredFieldsFromAdminQuery = `SELECT ${fromFields} FROM User WHERE Username='${username}'`;
|
|
85
|
+
const result = await connection.query(requiredFieldsFromAdminQuery);
|
|
86
|
+
logger.debug('Successfully retrieved the admin user for this org.');
|
|
87
|
+
if (result.totalSize === 1) {
|
|
88
|
+
const results = (0, kit_1.mapKeys)(result.records[0], (value, key) => (0, kit_1.lowerFirst)(key));
|
|
89
|
+
const fields = {
|
|
90
|
+
id: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.id]),
|
|
91
|
+
username,
|
|
92
|
+
alias: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.alias]),
|
|
93
|
+
email: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.email]),
|
|
94
|
+
emailEncodingKey: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.emailEncodingKey]),
|
|
95
|
+
languageLocaleKey: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.languageLocaleKey]),
|
|
96
|
+
localeSidKey: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.localeSidKey]),
|
|
97
|
+
profileId: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.profileId]),
|
|
98
|
+
lastName: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.lastName]),
|
|
99
|
+
timeZoneSidKey: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.timeZoneSidKey]),
|
|
100
|
+
};
|
|
101
|
+
return fields;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
throw messages.createError('userQueryFailed', [username]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Gets the profile id associated with a profile name.
|
|
109
|
+
*
|
|
110
|
+
* @param name The name of the profile.
|
|
111
|
+
* @param connection The connection for the query.
|
|
112
|
+
*/
|
|
113
|
+
async function retrieveProfileId(name, connection) {
|
|
114
|
+
if (!sfdc_1.sfdc.validateSalesforceId(name)) {
|
|
115
|
+
const profileQuery = `SELECT Id FROM Profile WHERE name='${name}'`;
|
|
116
|
+
const result = await connection.query(profileQuery);
|
|
117
|
+
if (result.records.length > 0) {
|
|
118
|
+
return result.records[0].Id;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return name;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Provides a default set of fields values that can be used to create a user. This is handy for
|
|
125
|
+
* software development purposes.
|
|
126
|
+
*
|
|
127
|
+
* ```
|
|
128
|
+
* const connection: Connection = await Connection.create({
|
|
129
|
+
* authInfo: await AuthInfo.create({ username: 'user@example.com' })
|
|
130
|
+
* });
|
|
131
|
+
* const org: Org = await Org.create({ connection });
|
|
132
|
+
* const options: DefaultUserFields.Options = {
|
|
133
|
+
* templateUser: org.getUsername()
|
|
134
|
+
* };
|
|
135
|
+
* const fields = (await DefaultUserFields.create(options)).getFields();
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
class DefaultUserFields extends kit_1.AsyncCreatable {
|
|
139
|
+
/**
|
|
140
|
+
* @ignore
|
|
141
|
+
*/
|
|
142
|
+
constructor(options) {
|
|
143
|
+
super(options);
|
|
144
|
+
this.options = options || { templateUser: '' };
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get user fields.
|
|
148
|
+
*/
|
|
149
|
+
getFields() {
|
|
150
|
+
return this.userFields;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Initialize asynchronous components.
|
|
154
|
+
*/
|
|
155
|
+
async init() {
|
|
156
|
+
this.logger = await logger_1.Logger.child('DefaultUserFields');
|
|
157
|
+
this.userFields = await retrieveUserFields(this.logger, this.options.templateUser);
|
|
158
|
+
this.userFields.profileId = await retrieveProfileId('Standard User', await connection_1.Connection.create({
|
|
159
|
+
authInfo: await authInfo_1.AuthInfo.create({ username: this.options.templateUser }),
|
|
160
|
+
}));
|
|
161
|
+
this.logger.debug(`Standard User profileId: ${this.userFields.profileId}`);
|
|
162
|
+
if (this.options.newUserName) {
|
|
163
|
+
this.userFields.username = this.options.newUserName;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
this.userFields.username = `${Date.now()}_${this.userFields.username}`;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
exports.DefaultUserFields = DefaultUserFields;
|
|
171
|
+
/**
|
|
172
|
+
* A class for creating a User, generating a password for a user, and assigning a user to one or more permission sets.
|
|
173
|
+
* See methods for examples.
|
|
174
|
+
*/
|
|
175
|
+
class User extends kit_1.AsyncCreatable {
|
|
176
|
+
/**
|
|
177
|
+
* @ignore
|
|
178
|
+
*/
|
|
179
|
+
constructor(options) {
|
|
180
|
+
super(options);
|
|
181
|
+
this.org = options.org;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Generate default password for a user. Returns An encrypted buffer containing a utf8 encoded password.
|
|
185
|
+
*/
|
|
186
|
+
static generatePasswordUtf8(passwordCondition = { length: 13, complexity: 5 }) {
|
|
187
|
+
if (!PASSWORD_COMPLEXITY[passwordCondition.complexity]) {
|
|
188
|
+
const msg = messages.getMessage('complexityOutOfBound');
|
|
189
|
+
throw new sfError_1.SfError(msg, 'complexityOutOfBound');
|
|
190
|
+
}
|
|
191
|
+
if (passwordCondition.length < 8 || passwordCondition.length > 1000) {
|
|
192
|
+
const msg = messages.getMessage('lengthOutOfBound');
|
|
193
|
+
throw new sfError_1.SfError(msg, 'lengthOutOfBound');
|
|
194
|
+
}
|
|
195
|
+
let password = [];
|
|
196
|
+
['SYMBOLS', 'NUMBERS', 'UPPER', 'LOWER'].forEach((charSet) => {
|
|
197
|
+
if (PASSWORD_COMPLEXITY[passwordCondition.complexity][charSet]) {
|
|
198
|
+
password.push(CHARACTERS[charSet][rand(CHARACTERS[charSet])]);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
// Concatinating remaining length randomly with all lower characters
|
|
202
|
+
password = password.concat(Array(Math.max(passwordCondition.length - password.length, 0))
|
|
203
|
+
.fill('0')
|
|
204
|
+
.map(() => CHARACTERS['LOWER'][rand(CHARACTERS['LOWER'])]));
|
|
205
|
+
password = password.sort(() => Math.random() - 0.5);
|
|
206
|
+
const secureBuffer = new secureBuffer_1.SecureBuffer();
|
|
207
|
+
secureBuffer.consume(Buffer.from(password.join(''), 'utf8'));
|
|
208
|
+
return secureBuffer;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Initialize a new instance of a user and return it.
|
|
212
|
+
*/
|
|
213
|
+
async init() {
|
|
214
|
+
this.logger = await logger_1.Logger.child('User');
|
|
215
|
+
await this.org.refreshAuth();
|
|
216
|
+
this.logger.debug('Auth refresh ok');
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Assigns a password to a user. For a user to have the ability to assign their own password, the org needs the
|
|
220
|
+
* following org feature: EnableSetPasswordInApi.
|
|
221
|
+
*
|
|
222
|
+
* @param info The AuthInfo object for user to assign the password to.
|
|
223
|
+
* @param password [throwWhenRemoveFails = User.generatePasswordUtf8()] A SecureBuffer containing the new password.
|
|
224
|
+
*/
|
|
225
|
+
async assignPassword(info, password = User.generatePasswordUtf8()) {
|
|
226
|
+
this.logger.debug(`Attempting to set password for userId: ${info.getFields().userId} username: ${info.getFields().username}`);
|
|
227
|
+
const userConnection = await connection_1.Connection.create({ authInfo: info });
|
|
228
|
+
return new Promise((resolve, reject) => {
|
|
229
|
+
// no promises in async method
|
|
230
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
231
|
+
password.value(async (buffer) => {
|
|
232
|
+
try {
|
|
233
|
+
const soap = userConnection.soap;
|
|
234
|
+
await soap.setPassword((0, ts_types_1.ensureString)(info.getFields().userId), buffer.toString('utf8'));
|
|
235
|
+
this.logger.debug(`Set password for userId: ${info.getFields().userId}`);
|
|
236
|
+
resolve();
|
|
237
|
+
}
|
|
238
|
+
catch (e) {
|
|
239
|
+
reject(e);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Methods to assign one or more permission set names to a user.
|
|
246
|
+
*
|
|
247
|
+
* @param id The Salesforce id of the user to assign the permission set to.
|
|
248
|
+
* @param permsetNames An array of permission set names.
|
|
249
|
+
*
|
|
250
|
+
* ```
|
|
251
|
+
* const username = 'user@example.com';
|
|
252
|
+
* const connection: Connection = await Connection.create({
|
|
253
|
+
* authInfo: await AuthInfo.create({ username })
|
|
254
|
+
* });
|
|
255
|
+
* const org = await Org.create({ connection });
|
|
256
|
+
* const user: User = await User.create({ org });
|
|
257
|
+
* const fields: UserFields = await user.retrieve(username);
|
|
258
|
+
* await user.assignPermissionSets(fields.id, ['sfdx', 'approver']);
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
async assignPermissionSets(id, permsetNames) {
|
|
262
|
+
if (!id) {
|
|
263
|
+
throw messages.createError('missingId');
|
|
264
|
+
}
|
|
265
|
+
if (!permsetNames) {
|
|
266
|
+
throw messages.createError('permsetNamesAreRequired');
|
|
267
|
+
}
|
|
268
|
+
const assignments = await permissionSetAssignment_1.PermissionSetAssignment.init(this.org);
|
|
269
|
+
await Promise.all(permsetNames.map((permsetName) => assignments.create(id, permsetName)));
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Method for creating a new User.
|
|
273
|
+
*
|
|
274
|
+
* By default scratch orgs only allow creating 2 additional users. Work with Salesforce Customer Service to increase
|
|
275
|
+
* user limits.
|
|
276
|
+
*
|
|
277
|
+
* The Org Preferences required to increase the number of users are:
|
|
278
|
+
* Standard User Licenses
|
|
279
|
+
* Salesforce CRM Content User
|
|
280
|
+
*
|
|
281
|
+
* @param fields The required fields for creating a user.
|
|
282
|
+
*
|
|
283
|
+
* ```
|
|
284
|
+
* const connection: Connection = await Connection.create({
|
|
285
|
+
* authInfo: await AuthInfo.create({ username: 'user@example.com' })
|
|
286
|
+
* });
|
|
287
|
+
* const org = await Org.create({ connection });
|
|
288
|
+
*
|
|
289
|
+
* const defaultUserFields = await DefaultUserFields.create({ templateUser: 'devhub_user@example.com' });
|
|
290
|
+
* const user: User = await User.create({ org });
|
|
291
|
+
* const info: AuthInfo = await user.createUser(defaultUserFields.getFields());
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
294
|
+
async createUser(fields) {
|
|
295
|
+
// Create a user and get a refresh token
|
|
296
|
+
const refreshTokenSecret = await this.createUserInternal(fields);
|
|
297
|
+
// Create the initial auth info
|
|
298
|
+
const authInfo = await authInfo_1.AuthInfo.create({ username: this.org.getUsername() });
|
|
299
|
+
const adminUserAuthFields = authInfo.getFields(true);
|
|
300
|
+
// Setup oauth options for the new user
|
|
301
|
+
const oauthOptions = {
|
|
302
|
+
// Communities users require the instance for auth
|
|
303
|
+
loginUrl: adminUserAuthFields.instanceUrl ?? adminUserAuthFields.loginUrl,
|
|
304
|
+
refreshToken: refreshTokenSecret.buffer.value((buffer) => buffer.toString('utf8')),
|
|
305
|
+
clientId: adminUserAuthFields.clientId,
|
|
306
|
+
clientSecret: adminUserAuthFields.clientSecret,
|
|
307
|
+
privateKey: adminUserAuthFields.privateKey,
|
|
308
|
+
};
|
|
309
|
+
// Create an auth info object for the new user
|
|
310
|
+
const newUserAuthInfo = await authInfo_1.AuthInfo.create({
|
|
311
|
+
username: fields.username,
|
|
312
|
+
oauth2Options: oauthOptions,
|
|
313
|
+
});
|
|
314
|
+
// Update the auth info object with created user id.
|
|
315
|
+
const newUserAuthFields = newUserAuthInfo.getFields();
|
|
316
|
+
newUserAuthFields.userId = refreshTokenSecret.userId;
|
|
317
|
+
// Make sure we can connect and if so save the auth info.
|
|
318
|
+
await this.describeUserAndSave(newUserAuthInfo);
|
|
319
|
+
// Let the org know there is a new user. See $HOME/.sfdx/[orgid].json for the mapping.
|
|
320
|
+
await this.org.addUsername(newUserAuthInfo);
|
|
321
|
+
return newUserAuthInfo;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Method to retrieve the UserFields for a user.
|
|
325
|
+
*
|
|
326
|
+
* @param username The username of the user.
|
|
327
|
+
*
|
|
328
|
+
* ```
|
|
329
|
+
* const username = 'boris@thecat.com';
|
|
330
|
+
* const connection: Connection = await Connection.create({
|
|
331
|
+
* authInfo: await AuthInfo.create({ username })
|
|
332
|
+
* });
|
|
333
|
+
* const org = await Org.create({ connection });
|
|
334
|
+
* const user: User = await User.create({ org });
|
|
335
|
+
* const fields: UserFields = await user.retrieve(username);
|
|
336
|
+
* ```
|
|
337
|
+
*/
|
|
338
|
+
async retrieve(username) {
|
|
339
|
+
return retrieveUserFields(this.logger, username);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Helper method that verifies the server's User object is available and if so allows persisting the Auth information.
|
|
343
|
+
*
|
|
344
|
+
* @param newUserAuthInfo The AuthInfo for the new user.
|
|
345
|
+
*/
|
|
346
|
+
async describeUserAndSave(newUserAuthInfo) {
|
|
347
|
+
const connection = await connection_1.Connection.create({ authInfo: newUserAuthInfo });
|
|
348
|
+
this.logger.debug(`Created connection for user: ${newUserAuthInfo.getUsername()}`);
|
|
349
|
+
const userDescribe = await connection.describe('User');
|
|
350
|
+
if (userDescribe?.fields) {
|
|
351
|
+
await newUserAuthInfo.save();
|
|
352
|
+
return newUserAuthInfo;
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
throw messages.createError('permsetNamesAreRequired');
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Helper that makes a REST request to create the user, and update additional required fields.
|
|
360
|
+
*
|
|
361
|
+
* @param fields The configuration the new user should have.
|
|
362
|
+
*/
|
|
363
|
+
async createUserInternal(fields) {
|
|
364
|
+
if (!fields) {
|
|
365
|
+
throw messages.createError('missingFields');
|
|
366
|
+
}
|
|
367
|
+
const conn = this.org.getConnection();
|
|
368
|
+
const body = JSON.stringify({
|
|
369
|
+
username: fields.username,
|
|
370
|
+
emails: [fields.email],
|
|
371
|
+
name: {
|
|
372
|
+
familyName: fields.lastName,
|
|
373
|
+
},
|
|
374
|
+
nickName: fields.username.substring(0, 40),
|
|
375
|
+
entitlements: [
|
|
376
|
+
{
|
|
377
|
+
value: fields.profileId,
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
});
|
|
381
|
+
this.logger.debug(`user create request body: ${body}`);
|
|
382
|
+
const scimUrl = conn.normalizeUrl(scimEndpoint);
|
|
383
|
+
this.logger.debug(`scimUrl: ${scimUrl}`);
|
|
384
|
+
const info = {
|
|
385
|
+
method: 'POST',
|
|
386
|
+
url: scimUrl,
|
|
387
|
+
headers: scimHeaders,
|
|
388
|
+
body,
|
|
389
|
+
};
|
|
390
|
+
const response = await this.rawRequest(conn, info);
|
|
391
|
+
const responseBody = (0, kit_1.parseJsonMap)((0, ts_types_1.ensureString)(response['body']));
|
|
392
|
+
const statusCode = (0, ts_types_1.asNumber)(response.statusCode);
|
|
393
|
+
this.logger.debug(`user create response.statusCode: ${response.statusCode}`);
|
|
394
|
+
if (!(statusCode === 201 || statusCode === 200)) {
|
|
395
|
+
let message = messages.getMessage('invalidHttpResponseCreatingUser', [statusCode]);
|
|
396
|
+
if (responseBody) {
|
|
397
|
+
const errors = (0, ts_types_1.asJsonArray)(responseBody.Errors);
|
|
398
|
+
if (errors && errors.length > 0) {
|
|
399
|
+
message = `${message} causes:${os_1.EOL}`;
|
|
400
|
+
errors.forEach((singleMessage) => {
|
|
401
|
+
if (!(0, ts_types_1.isJsonMap)(singleMessage))
|
|
402
|
+
return;
|
|
403
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
404
|
+
message = `${message}${os_1.EOL}${singleMessage.description}`;
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
this.logger.debug(message);
|
|
409
|
+
throw new sfError_1.SfError(message, 'UserCreateHttpError');
|
|
410
|
+
}
|
|
411
|
+
fields.id = (0, ts_types_1.ensureString)(responseBody.id);
|
|
412
|
+
await this.updateRequiredUserFields(fields);
|
|
413
|
+
const buffer = new secureBuffer_1.SecureBuffer();
|
|
414
|
+
const headers = (0, ts_types_1.ensureJsonMap)(response.headers);
|
|
415
|
+
const autoApproveUser = (0, ts_types_1.ensureString)(headers['auto-approve-user']);
|
|
416
|
+
buffer.consume(Buffer.from(autoApproveUser));
|
|
417
|
+
return {
|
|
418
|
+
buffer,
|
|
419
|
+
userId: fields.id,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
// eslint-disable-next-line class-methods-use-this
|
|
423
|
+
async rawRequest(conn, options) {
|
|
424
|
+
return new Promise((resolve, reject) => {
|
|
425
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
426
|
+
const httpApi = new http_api_1.HttpApi(conn, options);
|
|
427
|
+
httpApi.on('response', (response) => resolve(response));
|
|
428
|
+
httpApi.request(options).catch(reject);
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Update the remaining required fields for the user.
|
|
433
|
+
*
|
|
434
|
+
* @param fields The fields for the user.
|
|
435
|
+
*/
|
|
436
|
+
async updateRequiredUserFields(fields) {
|
|
437
|
+
const leftOverRequiredFields = (0, kit_1.omit)(fields, [
|
|
438
|
+
exports.REQUIRED_FIELDS.username,
|
|
439
|
+
exports.REQUIRED_FIELDS.email,
|
|
440
|
+
exports.REQUIRED_FIELDS.lastName,
|
|
441
|
+
exports.REQUIRED_FIELDS.profileId,
|
|
442
|
+
]);
|
|
443
|
+
const object = (0, kit_1.mapKeys)(leftOverRequiredFields, (value, key) => (0, kit_1.upperFirst)(key));
|
|
444
|
+
await this.org.getConnection().sobject('User').update(object);
|
|
445
|
+
this.logger.debug(`Successfully Updated additional properties for user: ${fields.username}`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
exports.User = User;
|
|
449
449
|
//# sourceMappingURL=user.js.map
|