@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.
Files changed (153) hide show
  1. package/LICENSE.txt +11 -11
  2. package/README.md +222 -222
  3. package/lib/config/aliasesConfig.d.ts +12 -12
  4. package/lib/config/aliasesConfig.js +27 -27
  5. package/lib/config/authInfoConfig.d.ts +19 -19
  6. package/lib/config/authInfoConfig.js +34 -34
  7. package/lib/config/config.d.ts +311 -311
  8. package/lib/config/config.js +574 -574
  9. package/lib/config/configAggregator.d.ts +232 -232
  10. package/lib/config/configAggregator.js +379 -379
  11. package/lib/config/configFile.d.ts +199 -199
  12. package/lib/config/configFile.js +340 -340
  13. package/lib/config/configGroup.d.ts +141 -141
  14. package/lib/config/configGroup.js +224 -224
  15. package/lib/config/configStore.d.ts +241 -241
  16. package/lib/config/configStore.js +352 -352
  17. package/lib/config/envVars.d.ts +101 -101
  18. package/lib/config/envVars.js +456 -456
  19. package/lib/config/orgUsersConfig.d.ts +31 -31
  20. package/lib/config/orgUsersConfig.js +41 -41
  21. package/lib/config/sandboxOrgConfig.d.ts +37 -37
  22. package/lib/config/sandboxOrgConfig.js +50 -50
  23. package/lib/config/sandboxProcessCache.d.ts +16 -16
  24. package/lib/config/sandboxProcessCache.js +37 -37
  25. package/lib/config/tokensConfig.d.ts +10 -10
  26. package/lib/config/tokensConfig.js +28 -28
  27. package/lib/config/ttlConfig.d.ts +34 -34
  28. package/lib/config/ttlConfig.js +54 -54
  29. package/lib/crypto/crypto.d.ts +54 -54
  30. package/lib/crypto/crypto.js +220 -220
  31. package/lib/crypto/keyChain.d.ts +8 -8
  32. package/lib/crypto/keyChain.js +61 -61
  33. package/lib/crypto/keyChainImpl.d.ts +116 -116
  34. package/lib/crypto/keyChainImpl.js +486 -486
  35. package/lib/crypto/secureBuffer.d.ts +46 -46
  36. package/lib/crypto/secureBuffer.js +82 -82
  37. package/lib/deviceOauthService.d.ts +71 -71
  38. package/lib/deviceOauthService.js +191 -191
  39. package/lib/exported.d.ts +38 -38
  40. package/lib/exported.js +118 -118
  41. package/lib/global.d.ts +70 -70
  42. package/lib/global.js +109 -109
  43. package/lib/lifecycleEvents.d.ts +93 -93
  44. package/lib/lifecycleEvents.js +188 -188
  45. package/lib/logger.d.ts +381 -381
  46. package/lib/logger.js +734 -734
  47. package/lib/messages.d.ts +291 -291
  48. package/lib/messages.js +543 -543
  49. package/lib/org/authInfo.d.ts +344 -344
  50. package/lib/org/authInfo.js +892 -892
  51. package/lib/org/authRemover.d.ts +88 -88
  52. package/lib/org/authRemover.js +182 -182
  53. package/lib/org/connection.d.ts +197 -197
  54. package/lib/org/connection.js +395 -395
  55. package/lib/org/index.d.ts +6 -6
  56. package/lib/org/index.js +28 -28
  57. package/lib/org/org.d.ts +558 -558
  58. package/lib/org/org.js +1267 -1267
  59. package/lib/org/orgConfigProperties.d.ts +69 -69
  60. package/lib/org/orgConfigProperties.js +136 -136
  61. package/lib/org/permissionSetAssignment.d.ts +35 -35
  62. package/lib/org/permissionSetAssignment.js +125 -125
  63. package/lib/org/scratchOrgCache.d.ts +20 -20
  64. package/lib/org/scratchOrgCache.js +32 -32
  65. package/lib/org/scratchOrgCreate.d.ts +54 -54
  66. package/lib/org/scratchOrgCreate.js +216 -216
  67. package/lib/org/scratchOrgErrorCodes.d.ts +10 -10
  68. package/lib/org/scratchOrgErrorCodes.js +88 -88
  69. package/lib/org/scratchOrgFeatureDeprecation.d.ts +26 -26
  70. package/lib/org/scratchOrgFeatureDeprecation.js +109 -109
  71. package/lib/org/scratchOrgInfoApi.d.ts +68 -68
  72. package/lib/org/scratchOrgInfoApi.js +413 -413
  73. package/lib/org/scratchOrgInfoGenerator.d.ts +64 -64
  74. package/lib/org/scratchOrgInfoGenerator.js +241 -241
  75. package/lib/org/scratchOrgLifecycleEvents.d.ts +10 -10
  76. package/lib/org/scratchOrgLifecycleEvents.js +40 -40
  77. package/lib/org/scratchOrgSettingsGenerator.d.ts +78 -78
  78. package/lib/org/scratchOrgSettingsGenerator.js +276 -276
  79. package/lib/org/scratchOrgTypes.d.ts +43 -43
  80. package/lib/org/scratchOrgTypes.js +8 -8
  81. package/lib/org/user.d.ts +187 -187
  82. package/lib/org/user.js +448 -448
  83. package/lib/schema/printer.d.ts +79 -79
  84. package/lib/schema/printer.js +260 -260
  85. package/lib/schema/validator.d.ts +70 -70
  86. package/lib/schema/validator.js +169 -169
  87. package/lib/sfError.d.ts +73 -73
  88. package/lib/sfError.js +136 -136
  89. package/lib/sfProject.d.ts +357 -357
  90. package/lib/sfProject.js +671 -671
  91. package/lib/stateAggregator/accessors/aliasAccessor.d.ts +98 -98
  92. package/lib/stateAggregator/accessors/aliasAccessor.js +145 -145
  93. package/lib/stateAggregator/accessors/orgAccessor.d.ts +101 -101
  94. package/lib/stateAggregator/accessors/orgAccessor.js +240 -240
  95. package/lib/stateAggregator/accessors/sandboxAccessor.d.ts +8 -8
  96. package/lib/stateAggregator/accessors/sandboxAccessor.js +27 -27
  97. package/lib/stateAggregator/accessors/tokenAccessor.d.ts +63 -63
  98. package/lib/stateAggregator/accessors/tokenAccessor.js +79 -79
  99. package/lib/stateAggregator/index.d.ts +4 -4
  100. package/lib/stateAggregator/index.js +26 -26
  101. package/lib/stateAggregator/stateAggregator.d.ts +25 -25
  102. package/lib/stateAggregator/stateAggregator.js +45 -45
  103. package/lib/status/myDomainResolver.d.ts +66 -66
  104. package/lib/status/myDomainResolver.js +124 -124
  105. package/lib/status/pollingClient.d.ts +85 -85
  106. package/lib/status/pollingClient.js +115 -115
  107. package/lib/status/streamingClient.d.ts +244 -244
  108. package/lib/status/streamingClient.js +436 -436
  109. package/lib/status/types.d.ts +89 -89
  110. package/lib/status/types.js +17 -17
  111. package/lib/testSetup.d.ts +553 -553
  112. package/lib/testSetup.js +871 -871
  113. package/lib/util/cache.d.ts +11 -11
  114. package/lib/util/cache.js +69 -69
  115. package/lib/util/checkLightningDomain.d.ts +1 -1
  116. package/lib/util/checkLightningDomain.js +28 -28
  117. package/lib/util/directoryWriter.d.ts +12 -12
  118. package/lib/util/directoryWriter.js +53 -53
  119. package/lib/util/getJwtAudienceUrl.d.ts +4 -4
  120. package/lib/util/getJwtAudienceUrl.js +18 -18
  121. package/lib/util/internal.d.ts +58 -58
  122. package/lib/util/internal.js +118 -118
  123. package/lib/util/jsonXmlTools.d.ts +14 -14
  124. package/lib/util/jsonXmlTools.js +38 -38
  125. package/lib/util/mapKeys.d.ts +14 -14
  126. package/lib/util/mapKeys.js +51 -51
  127. package/lib/util/sfdc.d.ts +52 -52
  128. package/lib/util/sfdc.js +85 -85
  129. package/lib/util/sfdcUrl.d.ts +72 -72
  130. package/lib/util/sfdcUrl.js +215 -215
  131. package/lib/util/structuredWriter.d.ts +9 -9
  132. package/lib/util/structuredWriter.js +2 -2
  133. package/lib/util/zipWriter.d.ts +16 -16
  134. package/lib/util/zipWriter.js +67 -67
  135. package/lib/webOAuthServer.d.ts +156 -156
  136. package/lib/webOAuthServer.js +388 -388
  137. package/messages/auth.md +37 -37
  138. package/messages/config.md +156 -156
  139. package/messages/connection.md +30 -30
  140. package/messages/core.json +20 -20
  141. package/messages/core.md +67 -67
  142. package/messages/encryption.md +85 -85
  143. package/messages/envVars.md +303 -303
  144. package/messages/org.md +63 -63
  145. package/messages/permissionSetAssignment.md +31 -31
  146. package/messages/scratchOrgCreate.md +23 -23
  147. package/messages/scratchOrgErrorCodes.md +115 -115
  148. package/messages/scratchOrgFeatureDeprecation.md +11 -11
  149. package/messages/scratchOrgInfoApi.md +15 -15
  150. package/messages/scratchOrgInfoGenerator.md +23 -23
  151. package/messages/streaming.md +23 -23
  152. package/messages/user.md +35 -35
  153. package/package.json +97 -97
@@ -1,389 +1,389 @@
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
- /* eslint-disable class-methods-use-this */
9
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
10
- Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.WebServer = exports.WebOAuthServer = void 0;
12
- const http = require("http");
13
- const querystring_1 = require("querystring");
14
- const url_1 = require("url");
15
- const net_1 = require("net");
16
- const jsforce_1 = require("jsforce");
17
- const kit_1 = require("@salesforce/kit");
18
- const ts_types_1 = require("@salesforce/ts-types");
19
- const logger_1 = require("./logger");
20
- const org_1 = require("./org");
21
- const sfError_1 = require("./sfError");
22
- const messages_1 = require("./messages");
23
- const sfProject_1 = require("./sfProject");
24
- messages_1.Messages.importMessagesDirectory(__dirname);
25
- const messages = messages_1.Messages.load('@salesforce/core', 'auth', [
26
- 'invalidRequestUri',
27
- 'invalidRequestMethod',
28
- 'missingAuthCode',
29
- 'serverErrorHTMLResponse',
30
- 'portInUse',
31
- 'portInUse.actions',
32
- ]);
33
- /**
34
- * Handles the creation of a web server for web based login flows.
35
- *
36
- * Usage:
37
- * ```
38
- * const oauthConfig = {
39
- * loginUrl: this.flags.instanceurl,
40
- * clientId: this.flags.clientid,
41
- * };
42
- *
43
- * const oauthServer = await WebOAuthServer.create({ oauthConfig });
44
- * await oauthServer.start();
45
- * await open(oauthServer.getAuthorizationUrl(), { wait: false });
46
- * const authInfo = await oauthServer.authorizeAndSave();
47
- * ```
48
- */
49
- class WebOAuthServer extends kit_1.AsyncCreatable {
50
- constructor(options) {
51
- super(options);
52
- this.oauthConfig = options.oauthConfig;
53
- }
54
- /**
55
- * Returns the configured oauthLocalPort or the WebOAuthServer.DEFAULT_PORT
56
- *
57
- * @returns {Promise<number>}
58
- */
59
- static async determineOauthPort() {
60
- try {
61
- const sfProject = await sfProject_1.SfProjectJson.create();
62
- return sfProject.get('oauthLocalPort') || WebOAuthServer.DEFAULT_PORT;
63
- }
64
- catch {
65
- return WebOAuthServer.DEFAULT_PORT;
66
- }
67
- }
68
- /**
69
- * Returns the authorization url that's used for the login flow
70
- *
71
- * @returns {string}
72
- */
73
- getAuthorizationUrl() {
74
- return this.authUrl;
75
- }
76
- /**
77
- * Executes the oauth request and creates a new AuthInfo when successful
78
- *
79
- * @returns {Promise<AuthInfo>}
80
- */
81
- async authorizeAndSave() {
82
- if (!this.webServer.server)
83
- await this.start();
84
- return new Promise((resolve, reject) => {
85
- const handler = () => {
86
- this.logger.debug(`OAuth web login service listening on port: ${this.webServer.port}`);
87
- this.executeOauthRequest()
88
- .then(async (response) => {
89
- try {
90
- const authInfo = await org_1.AuthInfo.create({
91
- oauth2Options: this.oauthConfig,
92
- oauth2: this.oauth2,
93
- });
94
- await authInfo.save();
95
- this.webServer.doRedirect(303, authInfo.getOrgFrontDoorUrl(), response);
96
- response.end();
97
- resolve(authInfo);
98
- }
99
- catch (err) {
100
- this.webServer.reportError(err, response);
101
- reject(err);
102
- }
103
- })
104
- .catch((err) => {
105
- this.logger.debug('error reported, closing server connection and re-throwing');
106
- reject(err);
107
- })
108
- .finally(() => {
109
- this.logger.debug('closing server connection');
110
- this.webServer.close();
111
- });
112
- };
113
- // if the server is already listening the listening event won't be fired anymore so execute handler() directly
114
- if (this.webServer.server.listening) {
115
- handler();
116
- }
117
- else {
118
- this.webServer.server.once('listening', handler);
119
- }
120
- });
121
- }
122
- /**
123
- * Starts the web server
124
- */
125
- async start() {
126
- await this.webServer.start();
127
- }
128
- async init() {
129
- this.logger = await logger_1.Logger.child(this.constructor.name);
130
- const port = await WebOAuthServer.determineOauthPort();
131
- if (!this.oauthConfig.clientId)
132
- this.oauthConfig.clientId = org_1.DEFAULT_CONNECTED_APP_INFO.clientId;
133
- if (!this.oauthConfig.loginUrl)
134
- this.oauthConfig.loginUrl = org_1.AuthInfo.getDefaultInstanceUrl();
135
- if (!this.oauthConfig.redirectUri)
136
- this.oauthConfig.redirectUri = `http://localhost:${port}/OauthRedirect`;
137
- this.webServer = await WebServer.create({ port });
138
- this.oauth2 = new jsforce_1.OAuth2(this.oauthConfig);
139
- this.authUrl = org_1.AuthInfo.getAuthorizationUrl(this.oauthConfig, this.oauth2);
140
- }
141
- /**
142
- * Executes the oauth request
143
- *
144
- * @returns {Promise<AuthInfo>}
145
- */
146
- async executeOauthRequest() {
147
- return new Promise((resolve, reject) => {
148
- this.logger.debug('Starting web auth flow');
149
- // eslint-disable-next-line @typescript-eslint/no-misused-promises, @typescript-eslint/no-explicit-any, @typescript-eslint/require-await
150
- this.webServer.server.on('request', async (request, response) => {
151
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
152
- const url = (0, url_1.parse)(request.url);
153
- this.logger.debug(`processing request for uri: ${url.pathname}`);
154
- if (request.method === 'GET') {
155
- if (url.pathname?.startsWith('/OauthRedirect')) {
156
- request.query = (0, querystring_1.parse)(url.query);
157
- if (request.query.error) {
158
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
159
- const err = new sfError_1.SfError(request.query.error_description ?? request.query.error, request.query.error);
160
- this.webServer.reportError(err, response);
161
- return reject(err);
162
- }
163
- this.logger.debug(`request.query.state: ${request.query.state}`);
164
- try {
165
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
166
- this.oauthConfig.authCode = (0, ts_types_1.asString)(this.parseAuthCodeFromRequest(response, request));
167
- resolve(response);
168
- }
169
- catch (err) {
170
- reject(err);
171
- }
172
- }
173
- else {
174
- this.webServer.sendError(404, 'Resource not found', response);
175
- const errName = 'invalidRequestUri';
176
- const errMessage = messages.getMessage(errName, [url.pathname]);
177
- reject(new sfError_1.SfError(errMessage, errName));
178
- }
179
- }
180
- else {
181
- this.webServer.sendError(405, 'Unsupported http methods', response);
182
- const errName = 'invalidRequestMethod';
183
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
184
- const errMessage = messages.getMessage(errName, [request.method]);
185
- reject(new sfError_1.SfError(errMessage, errName));
186
- }
187
- });
188
- });
189
- }
190
- /**
191
- * Parses the auth code from the request url
192
- *
193
- * @param response the http response
194
- * @param request the http request
195
- * @returns {Nullable<string>}
196
- */
197
- parseAuthCodeFromRequest(response, request) {
198
- if (!this.validateState(request)) {
199
- const error = new sfError_1.SfError('urlStateMismatch');
200
- this.webServer.sendError(400, `${error.message}\n`, response);
201
- this.closeRequest(request);
202
- this.logger.warn('urlStateMismatchAttempt detected.');
203
- if (!(0, ts_types_1.get)(this.webServer.server, 'urlStateMismatchAttempt')) {
204
- this.logger.error(error.message);
205
- (0, kit_1.set)(this.webServer.server, 'urlStateMismatchAttempt', true);
206
- }
207
- }
208
- else {
209
- const authCode = request.query.code;
210
- if (authCode && authCode.length > 4) {
211
- // AuthCodes are generally long strings. For security purposes we will just log the last 4 of the auth code.
212
- this.logger.debug(`Successfully obtained auth code: ...${authCode.substring(authCode.length - 5)}`);
213
- }
214
- else {
215
- this.logger.debug('Expected an auth code but could not find one.');
216
- throw messages.createError('missingAuthCode');
217
- }
218
- this.logger.debug(`oauthConfig.loginUrl: ${this.oauthConfig.loginUrl}`);
219
- this.logger.debug(`oauthConfig.clientId: ${this.oauthConfig.clientId}`);
220
- this.logger.debug(`oauthConfig.redirectUri: ${this.oauthConfig.redirectUri}`);
221
- return authCode;
222
- }
223
- return null;
224
- }
225
- /**
226
- * Closes the request
227
- *
228
- * @param request the http request
229
- */
230
- closeRequest(request) {
231
- request.connection.end();
232
- request.connection.destroy();
233
- }
234
- /**
235
- * Validates that the state param in the auth url matches the state
236
- * param in the http request
237
- *
238
- * @param request the http request
239
- */
240
- validateState(request) {
241
- const state = request.query.state;
242
- const query = (0, url_1.parse)(this.authUrl, true).query;
243
- return !!(state && state === query.state);
244
- }
245
- }
246
- exports.WebOAuthServer = WebOAuthServer;
247
- WebOAuthServer.DEFAULT_PORT = 1717;
248
- /**
249
- * Handles the actions specific to the http server
250
- */
251
- class WebServer extends kit_1.AsyncCreatable {
252
- constructor(options) {
253
- super(options);
254
- this.port = WebOAuthServer.DEFAULT_PORT;
255
- this.host = 'localhost';
256
- this.sockets = [];
257
- if (options.port)
258
- this.port = options.port;
259
- if (options.host)
260
- this.host = options.host;
261
- }
262
- /**
263
- * Starts the http server after checking that the port is open
264
- */
265
- async start() {
266
- try {
267
- this.logger.debug('Starting web server');
268
- await this.checkOsPort();
269
- this.logger.debug(`Nothing listening on host: localhost port: ${this.port} - good!`);
270
- this.server = http.createServer();
271
- this.server.on('connection', (socket) => {
272
- this.logger.debug(`socket connection initialized from ${socket.remoteAddress}`);
273
- this.sockets.push(socket);
274
- });
275
- this.server.listen(this.port, this.host);
276
- }
277
- catch (err) {
278
- if (err.name === 'EADDRINUSE') {
279
- throw messages.createError('portInUse', [this.port], [this.port]);
280
- }
281
- else {
282
- throw err;
283
- }
284
- }
285
- }
286
- /**
287
- * Closes the http server and all open sockets
288
- */
289
- close() {
290
- this.sockets.forEach((socket) => {
291
- socket.end();
292
- socket.destroy();
293
- });
294
- this.server.getConnections((_, num) => {
295
- this.logger.debug(`number of connections open: ${num}`);
296
- });
297
- this.server.close();
298
- }
299
- /**
300
- * sends a response error.
301
- *
302
- * @param statusCode he statusCode for the response.
303
- * @param message the message for the http body.
304
- * @param response the response to write the error to.
305
- */
306
- sendError(status, message, response) {
307
- response.statusMessage = message;
308
- response.statusCode = status;
309
- response.end();
310
- }
311
- /**
312
- * sends a response redirect.
313
- *
314
- * @param statusCode the statusCode for the response.
315
- * @param url the url to redirect to.
316
- * @param response the response to write the redirect to.
317
- */
318
- doRedirect(status, url, response) {
319
- response.setHeader('Content-Type', 'text/plain');
320
- const body = `${status} - Redirecting to ${url}`;
321
- response.setHeader('Content-Length', Buffer.byteLength(body));
322
- response.writeHead(status, { Location: url });
323
- response.end(body);
324
- }
325
- /**
326
- * sends a response to the browser reporting an error.
327
- *
328
- * @param error the error
329
- * @param response the response to write the redirect to.
330
- */
331
- reportError(error, response) {
332
- response.setHeader('Content-Type', 'text/html');
333
- const body = messages.getMessage('serverErrorHTMLResponse', [error.message]);
334
- response.setHeader('Content-Length', Buffer.byteLength(body));
335
- response.end(body);
336
- }
337
- async init() {
338
- this.logger = await logger_1.Logger.child(this.constructor.name);
339
- }
340
- /**
341
- * Make sure we can't open a socket on the localhost/host port. It's important because we don't want to send
342
- * auth tokens to a random strange port listener. We want to make sure we can startup our server first.
343
- *
344
- * @private
345
- */
346
- async checkOsPort() {
347
- return new Promise((resolve, reject) => {
348
- const clientConfig = { port: this.port, host: this.host };
349
- const socket = new net_1.Socket();
350
- socket.setTimeout(this.getSocketTimeout(), () => {
351
- socket.destroy();
352
- const error = new sfError_1.SfError('timeout', 'SOCKET_TIMEOUT');
353
- reject(error);
354
- });
355
- // An existing connection, means that the port is occupied
356
- socket.connect(clientConfig, () => {
357
- socket.destroy();
358
- const error = new sfError_1.SfError('Address in use', 'EADDRINUSE');
359
- error.data = {
360
- port: clientConfig.port,
361
- address: clientConfig.host,
362
- };
363
- reject(error);
364
- });
365
- // An error means that no existing connection exists, which is what we want
366
- socket.on('error', () => {
367
- // eslint-disable-next-line no-console
368
- socket.destroy();
369
- resolve(this.port);
370
- });
371
- });
372
- }
373
- /**
374
- * check and get the socket timeout form what was set in process.env.SFDX_HTTP_SOCKET_TIMEOUT
375
- *
376
- * @returns {number} - represents the socket timeout in ms
377
- * @private
378
- */
379
- getSocketTimeout() {
380
- const env = new kit_1.Env();
381
- const socketTimeout = (0, kit_1.toNumber)(env.getNumber('SFDX_HTTP_SOCKET_TIMEOUT'));
382
- return Number.isInteger(socketTimeout) && socketTimeout > 0
383
- ? socketTimeout
384
- : WebServer.DEFAULT_CLIENT_SOCKET_TIMEOUT;
385
- }
386
- }
387
- exports.WebServer = WebServer;
388
- WebServer.DEFAULT_CLIENT_SOCKET_TIMEOUT = 20000;
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
+ /* eslint-disable class-methods-use-this */
9
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.WebServer = exports.WebOAuthServer = void 0;
12
+ const http = require("http");
13
+ const querystring_1 = require("querystring");
14
+ const url_1 = require("url");
15
+ const net_1 = require("net");
16
+ const jsforce_1 = require("jsforce");
17
+ const kit_1 = require("@salesforce/kit");
18
+ const ts_types_1 = require("@salesforce/ts-types");
19
+ const logger_1 = require("./logger");
20
+ const org_1 = require("./org");
21
+ const sfError_1 = require("./sfError");
22
+ const messages_1 = require("./messages");
23
+ const sfProject_1 = require("./sfProject");
24
+ messages_1.Messages.importMessagesDirectory(__dirname);
25
+ const messages = messages_1.Messages.load('@salesforce/core', 'auth', [
26
+ 'invalidRequestUri',
27
+ 'invalidRequestMethod',
28
+ 'missingAuthCode',
29
+ 'serverErrorHTMLResponse',
30
+ 'portInUse',
31
+ 'portInUse.actions',
32
+ ]);
33
+ /**
34
+ * Handles the creation of a web server for web based login flows.
35
+ *
36
+ * Usage:
37
+ * ```
38
+ * const oauthConfig = {
39
+ * loginUrl: this.flags.instanceurl,
40
+ * clientId: this.flags.clientid,
41
+ * };
42
+ *
43
+ * const oauthServer = await WebOAuthServer.create({ oauthConfig });
44
+ * await oauthServer.start();
45
+ * await open(oauthServer.getAuthorizationUrl(), { wait: false });
46
+ * const authInfo = await oauthServer.authorizeAndSave();
47
+ * ```
48
+ */
49
+ class WebOAuthServer extends kit_1.AsyncCreatable {
50
+ constructor(options) {
51
+ super(options);
52
+ this.oauthConfig = options.oauthConfig;
53
+ }
54
+ /**
55
+ * Returns the configured oauthLocalPort or the WebOAuthServer.DEFAULT_PORT
56
+ *
57
+ * @returns {Promise<number>}
58
+ */
59
+ static async determineOauthPort() {
60
+ try {
61
+ const sfProject = await sfProject_1.SfProjectJson.create();
62
+ return sfProject.get('oauthLocalPort') || WebOAuthServer.DEFAULT_PORT;
63
+ }
64
+ catch {
65
+ return WebOAuthServer.DEFAULT_PORT;
66
+ }
67
+ }
68
+ /**
69
+ * Returns the authorization url that's used for the login flow
70
+ *
71
+ * @returns {string}
72
+ */
73
+ getAuthorizationUrl() {
74
+ return this.authUrl;
75
+ }
76
+ /**
77
+ * Executes the oauth request and creates a new AuthInfo when successful
78
+ *
79
+ * @returns {Promise<AuthInfo>}
80
+ */
81
+ async authorizeAndSave() {
82
+ if (!this.webServer.server)
83
+ await this.start();
84
+ return new Promise((resolve, reject) => {
85
+ const handler = () => {
86
+ this.logger.debug(`OAuth web login service listening on port: ${this.webServer.port}`);
87
+ this.executeOauthRequest()
88
+ .then(async (response) => {
89
+ try {
90
+ const authInfo = await org_1.AuthInfo.create({
91
+ oauth2Options: this.oauthConfig,
92
+ oauth2: this.oauth2,
93
+ });
94
+ await authInfo.save();
95
+ this.webServer.doRedirect(303, authInfo.getOrgFrontDoorUrl(), response);
96
+ response.end();
97
+ resolve(authInfo);
98
+ }
99
+ catch (err) {
100
+ this.webServer.reportError(err, response);
101
+ reject(err);
102
+ }
103
+ })
104
+ .catch((err) => {
105
+ this.logger.debug('error reported, closing server connection and re-throwing');
106
+ reject(err);
107
+ })
108
+ .finally(() => {
109
+ this.logger.debug('closing server connection');
110
+ this.webServer.close();
111
+ });
112
+ };
113
+ // if the server is already listening the listening event won't be fired anymore so execute handler() directly
114
+ if (this.webServer.server.listening) {
115
+ handler();
116
+ }
117
+ else {
118
+ this.webServer.server.once('listening', handler);
119
+ }
120
+ });
121
+ }
122
+ /**
123
+ * Starts the web server
124
+ */
125
+ async start() {
126
+ await this.webServer.start();
127
+ }
128
+ async init() {
129
+ this.logger = await logger_1.Logger.child(this.constructor.name);
130
+ const port = await WebOAuthServer.determineOauthPort();
131
+ if (!this.oauthConfig.clientId)
132
+ this.oauthConfig.clientId = org_1.DEFAULT_CONNECTED_APP_INFO.clientId;
133
+ if (!this.oauthConfig.loginUrl)
134
+ this.oauthConfig.loginUrl = org_1.AuthInfo.getDefaultInstanceUrl();
135
+ if (!this.oauthConfig.redirectUri)
136
+ this.oauthConfig.redirectUri = `http://localhost:${port}/OauthRedirect`;
137
+ this.webServer = await WebServer.create({ port });
138
+ this.oauth2 = new jsforce_1.OAuth2(this.oauthConfig);
139
+ this.authUrl = org_1.AuthInfo.getAuthorizationUrl(this.oauthConfig, this.oauth2);
140
+ }
141
+ /**
142
+ * Executes the oauth request
143
+ *
144
+ * @returns {Promise<AuthInfo>}
145
+ */
146
+ async executeOauthRequest() {
147
+ return new Promise((resolve, reject) => {
148
+ this.logger.debug('Starting web auth flow');
149
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises, @typescript-eslint/no-explicit-any, @typescript-eslint/require-await
150
+ this.webServer.server.on('request', async (request, response) => {
151
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
152
+ const url = (0, url_1.parse)(request.url);
153
+ this.logger.debug(`processing request for uri: ${url.pathname}`);
154
+ if (request.method === 'GET') {
155
+ if (url.pathname?.startsWith('/OauthRedirect')) {
156
+ request.query = (0, querystring_1.parse)(url.query);
157
+ if (request.query.error) {
158
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
159
+ const err = new sfError_1.SfError(request.query.error_description ?? request.query.error, request.query.error);
160
+ this.webServer.reportError(err, response);
161
+ return reject(err);
162
+ }
163
+ this.logger.debug(`request.query.state: ${request.query.state}`);
164
+ try {
165
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
166
+ this.oauthConfig.authCode = (0, ts_types_1.asString)(this.parseAuthCodeFromRequest(response, request));
167
+ resolve(response);
168
+ }
169
+ catch (err) {
170
+ reject(err);
171
+ }
172
+ }
173
+ else {
174
+ this.webServer.sendError(404, 'Resource not found', response);
175
+ const errName = 'invalidRequestUri';
176
+ const errMessage = messages.getMessage(errName, [url.pathname]);
177
+ reject(new sfError_1.SfError(errMessage, errName));
178
+ }
179
+ }
180
+ else {
181
+ this.webServer.sendError(405, 'Unsupported http methods', response);
182
+ const errName = 'invalidRequestMethod';
183
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
184
+ const errMessage = messages.getMessage(errName, [request.method]);
185
+ reject(new sfError_1.SfError(errMessage, errName));
186
+ }
187
+ });
188
+ });
189
+ }
190
+ /**
191
+ * Parses the auth code from the request url
192
+ *
193
+ * @param response the http response
194
+ * @param request the http request
195
+ * @returns {Nullable<string>}
196
+ */
197
+ parseAuthCodeFromRequest(response, request) {
198
+ if (!this.validateState(request)) {
199
+ const error = new sfError_1.SfError('urlStateMismatch');
200
+ this.webServer.sendError(400, `${error.message}\n`, response);
201
+ this.closeRequest(request);
202
+ this.logger.warn('urlStateMismatchAttempt detected.');
203
+ if (!(0, ts_types_1.get)(this.webServer.server, 'urlStateMismatchAttempt')) {
204
+ this.logger.error(error.message);
205
+ (0, kit_1.set)(this.webServer.server, 'urlStateMismatchAttempt', true);
206
+ }
207
+ }
208
+ else {
209
+ const authCode = request.query.code;
210
+ if (authCode && authCode.length > 4) {
211
+ // AuthCodes are generally long strings. For security purposes we will just log the last 4 of the auth code.
212
+ this.logger.debug(`Successfully obtained auth code: ...${authCode.substring(authCode.length - 5)}`);
213
+ }
214
+ else {
215
+ this.logger.debug('Expected an auth code but could not find one.');
216
+ throw messages.createError('missingAuthCode');
217
+ }
218
+ this.logger.debug(`oauthConfig.loginUrl: ${this.oauthConfig.loginUrl}`);
219
+ this.logger.debug(`oauthConfig.clientId: ${this.oauthConfig.clientId}`);
220
+ this.logger.debug(`oauthConfig.redirectUri: ${this.oauthConfig.redirectUri}`);
221
+ return authCode;
222
+ }
223
+ return null;
224
+ }
225
+ /**
226
+ * Closes the request
227
+ *
228
+ * @param request the http request
229
+ */
230
+ closeRequest(request) {
231
+ request.connection.end();
232
+ request.connection.destroy();
233
+ }
234
+ /**
235
+ * Validates that the state param in the auth url matches the state
236
+ * param in the http request
237
+ *
238
+ * @param request the http request
239
+ */
240
+ validateState(request) {
241
+ const state = request.query.state;
242
+ const query = (0, url_1.parse)(this.authUrl, true).query;
243
+ return !!(state && state === query.state);
244
+ }
245
+ }
246
+ exports.WebOAuthServer = WebOAuthServer;
247
+ WebOAuthServer.DEFAULT_PORT = 1717;
248
+ /**
249
+ * Handles the actions specific to the http server
250
+ */
251
+ class WebServer extends kit_1.AsyncCreatable {
252
+ constructor(options) {
253
+ super(options);
254
+ this.port = WebOAuthServer.DEFAULT_PORT;
255
+ this.host = 'localhost';
256
+ this.sockets = [];
257
+ if (options.port)
258
+ this.port = options.port;
259
+ if (options.host)
260
+ this.host = options.host;
261
+ }
262
+ /**
263
+ * Starts the http server after checking that the port is open
264
+ */
265
+ async start() {
266
+ try {
267
+ this.logger.debug('Starting web server');
268
+ await this.checkOsPort();
269
+ this.logger.debug(`Nothing listening on host: localhost port: ${this.port} - good!`);
270
+ this.server = http.createServer();
271
+ this.server.on('connection', (socket) => {
272
+ this.logger.debug(`socket connection initialized from ${socket.remoteAddress}`);
273
+ this.sockets.push(socket);
274
+ });
275
+ this.server.listen(this.port, this.host);
276
+ }
277
+ catch (err) {
278
+ if (err.name === 'EADDRINUSE') {
279
+ throw messages.createError('portInUse', [this.port], [this.port]);
280
+ }
281
+ else {
282
+ throw err;
283
+ }
284
+ }
285
+ }
286
+ /**
287
+ * Closes the http server and all open sockets
288
+ */
289
+ close() {
290
+ this.sockets.forEach((socket) => {
291
+ socket.end();
292
+ socket.destroy();
293
+ });
294
+ this.server.getConnections((_, num) => {
295
+ this.logger.debug(`number of connections open: ${num}`);
296
+ });
297
+ this.server.close();
298
+ }
299
+ /**
300
+ * sends a response error.
301
+ *
302
+ * @param statusCode he statusCode for the response.
303
+ * @param message the message for the http body.
304
+ * @param response the response to write the error to.
305
+ */
306
+ sendError(status, message, response) {
307
+ response.statusMessage = message;
308
+ response.statusCode = status;
309
+ response.end();
310
+ }
311
+ /**
312
+ * sends a response redirect.
313
+ *
314
+ * @param statusCode the statusCode for the response.
315
+ * @param url the url to redirect to.
316
+ * @param response the response to write the redirect to.
317
+ */
318
+ doRedirect(status, url, response) {
319
+ response.setHeader('Content-Type', 'text/plain');
320
+ const body = `${status} - Redirecting to ${url}`;
321
+ response.setHeader('Content-Length', Buffer.byteLength(body));
322
+ response.writeHead(status, { Location: url });
323
+ response.end(body);
324
+ }
325
+ /**
326
+ * sends a response to the browser reporting an error.
327
+ *
328
+ * @param error the error
329
+ * @param response the response to write the redirect to.
330
+ */
331
+ reportError(error, response) {
332
+ response.setHeader('Content-Type', 'text/html');
333
+ const body = messages.getMessage('serverErrorHTMLResponse', [error.message]);
334
+ response.setHeader('Content-Length', Buffer.byteLength(body));
335
+ response.end(body);
336
+ }
337
+ async init() {
338
+ this.logger = await logger_1.Logger.child(this.constructor.name);
339
+ }
340
+ /**
341
+ * Make sure we can't open a socket on the localhost/host port. It's important because we don't want to send
342
+ * auth tokens to a random strange port listener. We want to make sure we can startup our server first.
343
+ *
344
+ * @private
345
+ */
346
+ async checkOsPort() {
347
+ return new Promise((resolve, reject) => {
348
+ const clientConfig = { port: this.port, host: this.host };
349
+ const socket = new net_1.Socket();
350
+ socket.setTimeout(this.getSocketTimeout(), () => {
351
+ socket.destroy();
352
+ const error = new sfError_1.SfError('timeout', 'SOCKET_TIMEOUT');
353
+ reject(error);
354
+ });
355
+ // An existing connection, means that the port is occupied
356
+ socket.connect(clientConfig, () => {
357
+ socket.destroy();
358
+ const error = new sfError_1.SfError('Address in use', 'EADDRINUSE');
359
+ error.data = {
360
+ port: clientConfig.port,
361
+ address: clientConfig.host,
362
+ };
363
+ reject(error);
364
+ });
365
+ // An error means that no existing connection exists, which is what we want
366
+ socket.on('error', () => {
367
+ // eslint-disable-next-line no-console
368
+ socket.destroy();
369
+ resolve(this.port);
370
+ });
371
+ });
372
+ }
373
+ /**
374
+ * check and get the socket timeout form what was set in process.env.SFDX_HTTP_SOCKET_TIMEOUT
375
+ *
376
+ * @returns {number} - represents the socket timeout in ms
377
+ * @private
378
+ */
379
+ getSocketTimeout() {
380
+ const env = new kit_1.Env();
381
+ const socketTimeout = (0, kit_1.toNumber)(env.getNumber('SFDX_HTTP_SOCKET_TIMEOUT'));
382
+ return Number.isInteger(socketTimeout) && socketTimeout > 0
383
+ ? socketTimeout
384
+ : WebServer.DEFAULT_CLIENT_SOCKET_TIMEOUT;
385
+ }
386
+ }
387
+ exports.WebServer = WebServer;
388
+ WebServer.DEFAULT_CLIENT_SOCKET_TIMEOUT = 20000;
389
389
  //# sourceMappingURL=webOAuthServer.js.map