1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | Object.defineProperty(exports, "__esModule", { value: true });
|
9 | const kit_1 = require("@salesforce/kit");
|
10 | const ts_types_1 = require("@salesforce/ts-types");
|
11 | const os_1 = require("os");
|
12 | const authInfo_1 = require("./authInfo");
|
13 | const connection_1 = require("./connection");
|
14 | const logger_1 = require("./logger");
|
15 | const messages_1 = require("./messages");
|
16 | const permissionSetAssignment_1 = require("./permissionSetAssignment");
|
17 | const secureBuffer_1 = require("./secureBuffer");
|
18 | const sfdxError_1 = require("./sfdxError");
|
19 | const sfdc_1 = require("./util/sfdc");
|
20 | const PASSWORD_LENGTH = 10;
|
21 | const LOWER = 'abcdefghijklmnopqrstuvwxyz';
|
22 | const UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
23 | const NUMBERS = '1234567890';
|
24 | const SYMBOLS = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '[', ']', '|', '-'];
|
25 | const ALL = [LOWER, UPPER, NUMBERS, SYMBOLS.join('')];
|
26 | const rand = (len) => Math.floor(Math.random() * len.length);
|
27 | const scimEndpoint = '/services/scim/v1/Users';
|
28 | const scimHeaders = { 'auto-approve-user': 'true' };
|
29 |
|
30 |
|
31 |
|
32 | exports.REQUIRED_FIELDS = {
|
33 | id: 'id',
|
34 | username: 'username',
|
35 | lastName: 'lastName',
|
36 | alias: 'alias',
|
37 | timeZoneSidKey: 'timeZoneSidKey',
|
38 | localeSidKey: 'localeSidKey',
|
39 | emailEncodingKey: 'emailEncodingKey',
|
40 | profileId: 'profileId',
|
41 | languageLocaleKey: 'languageLocaleKey',
|
42 | email: 'email'
|
43 | };
|
44 |
|
45 |
|
46 |
|
47 |
|
48 | async function _retrieveUserFields(username) {
|
49 | const connection = await connection_1.Connection.create({
|
50 | authInfo: await authInfo_1.AuthInfo.create({ username })
|
51 | });
|
52 | const fromFields = Object.keys(exports.REQUIRED_FIELDS).map(kit_1.upperFirst);
|
53 | const requiredFieldsFromAdminQuery = `SELECT ${fromFields} FROM User WHERE Username='${username}'`;
|
54 | const result = await connection.query(requiredFieldsFromAdminQuery);
|
55 | this.logger.debug('Successfully retrieved the admin user for this org.');
|
56 | if (result.totalSize === 1) {
|
57 | const results = kit_1.mapKeys(result.records[0], (value, key) => kit_1.lowerFirst(key));
|
58 | const fields = {
|
59 | id: ts_types_1.ensure(ts_types_1.getString(results, exports.REQUIRED_FIELDS.id)),
|
60 | username,
|
61 | alias: ts_types_1.ensure(ts_types_1.getString(results, exports.REQUIRED_FIELDS.alias)),
|
62 | email: ts_types_1.ensure(ts_types_1.getString(results, exports.REQUIRED_FIELDS.email)),
|
63 | emailEncodingKey: ts_types_1.ensure(ts_types_1.getString(results, exports.REQUIRED_FIELDS.emailEncodingKey)),
|
64 | languageLocaleKey: ts_types_1.ensure(ts_types_1.getString(results, exports.REQUIRED_FIELDS.languageLocaleKey)),
|
65 | localeSidKey: ts_types_1.ensure(ts_types_1.getString(results, exports.REQUIRED_FIELDS.localeSidKey)),
|
66 | profileId: ts_types_1.ensure(ts_types_1.getString(results, exports.REQUIRED_FIELDS.profileId)),
|
67 | lastName: ts_types_1.ensure(ts_types_1.getString(results, exports.REQUIRED_FIELDS.lastName)),
|
68 | timeZoneSidKey: ts_types_1.ensure(ts_types_1.getString(results, exports.REQUIRED_FIELDS.timeZoneSidKey))
|
69 | };
|
70 | return fields;
|
71 | }
|
72 | else {
|
73 | throw sfdxError_1.SfdxError.create('@salesforce/core', 'user', 'userQueryFailed', [username]);
|
74 | }
|
75 | }
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 | async function _retrieveProfileId(name, connection) {
|
82 | if (!sfdc_1.sfdc.validateSalesforceId(name)) {
|
83 | const profileQuery = `SELECT Id FROM Profile WHERE name='${name}'`;
|
84 | const result = await connection.query(profileQuery);
|
85 | if (result.records.length > 0) {
|
86 | return result.records[0].Id;
|
87 | }
|
88 | }
|
89 | return name;
|
90 | }
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 | class DefaultUserFields extends kit_1.AsyncCreatable {
|
107 | |
108 |
|
109 |
|
110 | constructor(options) {
|
111 | super(options);
|
112 | this.options = options || { templateUser: '' };
|
113 | }
|
114 | |
115 |
|
116 |
|
117 | getFields() {
|
118 | return this.userFields;
|
119 | }
|
120 | |
121 |
|
122 |
|
123 | async init() {
|
124 | this.logger = await logger_1.Logger.child('DefaultUserFields');
|
125 | this.userFields = await _retrieveUserFields.call({ logger: this.logger }, this.options.templateUser);
|
126 | this.userFields.profileId = await _retrieveProfileId('Standard User', await connection_1.Connection.create({
|
127 | authInfo: await authInfo_1.AuthInfo.create({ username: this.options.templateUser })
|
128 | }));
|
129 | this.logger.debug(`Standard User profileId: ${this.userFields.profileId}`);
|
130 | if (this.options.newUserName) {
|
131 | this.userFields.username = this.options.newUserName;
|
132 | }
|
133 | else {
|
134 | this.userFields.username = `${Date.now()}_${this.userFields.username}`;
|
135 | }
|
136 | }
|
137 | }
|
138 | exports.DefaultUserFields = DefaultUserFields;
|
139 |
|
140 |
|
141 |
|
142 |
|
143 | class User extends kit_1.AsyncCreatable {
|
144 | |
145 |
|
146 |
|
147 | static generatePasswordUtf8() {
|
148 |
|
149 | const pass = Array(PASSWORD_LENGTH - ALL.length)
|
150 | .fill(9)
|
151 | .map(() => {
|
152 | const _set = ALL[rand(ALL)];
|
153 | return _set[rand(_set)];
|
154 | });
|
155 | const secureBuffer = new secureBuffer_1.SecureBuffer();
|
156 | secureBuffer.consume(Buffer.from(pass.join(''), 'utf8'));
|
157 | return secureBuffer;
|
158 | }
|
159 | |
160 |
|
161 |
|
162 | constructor(options) {
|
163 | super(options);
|
164 | this.org = options.org;
|
165 | }
|
166 | |
167 |
|
168 |
|
169 | async init() {
|
170 | this.logger = await logger_1.Logger.child('User');
|
171 | await this.org.refreshAuth();
|
172 | this.logger.debug('Auth refresh ok');
|
173 | }
|
174 | |
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 | async assignPassword(info, password = User.generatePasswordUtf8()) {
|
181 | this.logger.debug(`Attempting to set password for userId: ${info.getFields().userId} username: ${info.getFields().username}`);
|
182 | const userConnection = await connection_1.Connection.create({ authInfo: info });
|
183 | return new Promise((resolve, reject) => {
|
184 | password.value(async (buffer) => {
|
185 | try {
|
186 |
|
187 | const soap = userConnection.soap;
|
188 | await soap.setPassword(info.getFields().userId, buffer.toString('utf8'));
|
189 | this.logger.debug(`Set password for userId: ${info.getFields().userId}`);
|
190 | resolve();
|
191 | }
|
192 | catch (e) {
|
193 | reject(e);
|
194 | }
|
195 | });
|
196 | });
|
197 | }
|
198 | |
199 |
|
200 |
|
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 |
|
211 |
|
212 |
|
213 |
|
214 | async assignPermissionSets(id, permsetNames) {
|
215 | if (!id) {
|
216 | throw sfdxError_1.SfdxError.create('@salesforce/core', 'user', 'missingId');
|
217 | }
|
218 | if (!permsetNames) {
|
219 | throw sfdxError_1.SfdxError.create('@salesforce/core', 'user', 'permsetNamesAreRequired');
|
220 | }
|
221 | const assignments = await permissionSetAssignment_1.PermissionSetAssignment.init(this.org);
|
222 | for (const permsetName of permsetNames) {
|
223 | await assignments.create(id, permsetName);
|
224 | }
|
225 | }
|
226 | |
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 |
|
240 |
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 | async createUser(fields) {
|
250 |
|
251 | const refreshTokenSecret = await this.createUserInternal(fields);
|
252 |
|
253 | const adminUserAuthFields = this.org.getConnection().getAuthInfoFields();
|
254 |
|
255 | const oauthOptions = {
|
256 | loginUrl: adminUserAuthFields.loginUrl,
|
257 | refreshToken: refreshTokenSecret.buffer.value((buffer) => buffer.toString('utf8')),
|
258 | clientId: adminUserAuthFields.clientId,
|
259 | clientSecret: adminUserAuthFields.clientSecret,
|
260 | privateKey: adminUserAuthFields.privateKey
|
261 | };
|
262 |
|
263 | const newUserAuthInfo = await authInfo_1.AuthInfo.create({
|
264 | username: fields.username,
|
265 | oauth2Options: oauthOptions
|
266 | });
|
267 |
|
268 | const newUserAuthFields = newUserAuthInfo.getFields();
|
269 | newUserAuthFields.userId = refreshTokenSecret.userId;
|
270 |
|
271 | await this.describeUserAndSave(newUserAuthInfo);
|
272 |
|
273 | await this.org.addUsername(newUserAuthInfo);
|
274 | return newUserAuthInfo;
|
275 | }
|
276 | |
277 |
|
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 |
|
284 |
|
285 |
|
286 |
|
287 |
|
288 |
|
289 |
|
290 | async retrieve(username) {
|
291 | return await _retrieveUserFields.call(this, username);
|
292 | }
|
293 | |
294 |
|
295 |
|
296 |
|
297 | async describeUserAndSave(newUserAuthInfo) {
|
298 | const connection = await connection_1.Connection.create({ authInfo: newUserAuthInfo });
|
299 | this.logger.debug(`Created connection for user: ${newUserAuthInfo.getUsername()}`);
|
300 | const userDescribe = await connection.describe('User');
|
301 | if (userDescribe && userDescribe.fields) {
|
302 | await newUserAuthInfo.save();
|
303 | return newUserAuthInfo;
|
304 | }
|
305 | else {
|
306 | throw sfdxError_1.SfdxError.create('@salesforce/core', 'user', 'problemsDescribingTheUserObject');
|
307 | }
|
308 | }
|
309 | |
310 |
|
311 |
|
312 |
|
313 | async createUserInternal(fields) {
|
314 | if (!fields) {
|
315 | throw sfdxError_1.SfdxError.create('@salesforce/core', 'user', 'missingFields');
|
316 | }
|
317 | const body = JSON.stringify({
|
318 | username: fields.username,
|
319 | emails: [fields.email],
|
320 | name: {
|
321 | familyName: fields.lastName
|
322 | },
|
323 | nickName: fields.username.substring(0, 40),
|
324 | entitlements: [
|
325 | {
|
326 | value: fields.profileId
|
327 | }
|
328 | ]
|
329 | });
|
330 | this.logger.debug(`user create request body: ${body}`);
|
331 | const scimUrl = this.org.getConnection().normalizeUrl(scimEndpoint);
|
332 | this.logger.debug(`scimUrl: ${scimUrl}`);
|
333 | const info = {
|
334 | method: 'POST',
|
335 | url: scimUrl,
|
336 | headers: scimHeaders,
|
337 | body
|
338 | };
|
339 | const response = await this.org.getConnection().requestRaw(info);
|
340 | const responseBody = kit_1.parseJsonMap(ts_types_1.ensureString(response['body']));
|
341 | const statusCode = ts_types_1.asNumber(response.statusCode);
|
342 | this.logger.debug(`user create response.statusCode: ${response.statusCode}`);
|
343 | if (!(statusCode === 201 || statusCode === 200)) {
|
344 | const messages = messages_1.Messages.loadMessages('@salesforce/core', 'user');
|
345 | let message = messages.getMessage('invalidHttpResponseCreatingUser', [statusCode]);
|
346 | if (responseBody) {
|
347 | const errors = ts_types_1.asJsonArray(responseBody.Errors);
|
348 | if (errors && errors.length > 0) {
|
349 | message = `${message} causes:${os_1.EOL}`;
|
350 | errors.forEach(singleMessage => {
|
351 | if (!ts_types_1.isJsonMap(singleMessage))
|
352 | return;
|
353 | message = `${message}${os_1.EOL}${singleMessage.description}`;
|
354 | });
|
355 | }
|
356 | }
|
357 | this.logger.debug(message);
|
358 | throw new sfdxError_1.SfdxError(message, 'UserCreateHttpError');
|
359 | }
|
360 | fields.id = ts_types_1.ensureString(responseBody.id);
|
361 | await this.updateRequiredUserFields(fields);
|
362 | const buffer = new secureBuffer_1.SecureBuffer();
|
363 | const headers = ts_types_1.ensureJsonMap(response.headers);
|
364 | const autoApproveUser = ts_types_1.ensureString(headers['auto-approve-user']);
|
365 | buffer.consume(Buffer.from(autoApproveUser));
|
366 | return {
|
367 | buffer,
|
368 | userId: fields.id
|
369 | };
|
370 | }
|
371 | |
372 |
|
373 |
|
374 |
|
375 | async updateRequiredUserFields(fields) {
|
376 | const leftOverRequiredFields = kit_1.omit(fields, [
|
377 | exports.REQUIRED_FIELDS.username,
|
378 | exports.REQUIRED_FIELDS.email,
|
379 | exports.REQUIRED_FIELDS.lastName,
|
380 | exports.REQUIRED_FIELDS.profileId
|
381 | ]);
|
382 | const object = kit_1.mapKeys(leftOverRequiredFields, (value, key) => kit_1.upperFirst(key));
|
383 | await this.org
|
384 | .getConnection()
|
385 | .sobject('User')
|
386 | .update(object);
|
387 | this.logger.debug(`Successfully Updated additional properties for user: ${fields.username}`);
|
388 | }
|
389 | }
|
390 | exports.User = User;
|
391 |
|
\ | No newline at end of file |