@pega/custom-dx-components 2.0.23 → 2.0.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pega/custom-dx-components",
3
- "version": "2.0.23",
3
+ "version": "2.0.24",
4
4
  "description": "Utility for building custom UI components",
5
5
  "bin": "./bin/index.js",
6
6
  "scripts": {
@@ -31,7 +31,6 @@
31
31
  "file-loader": "6.2.0",
32
32
  "fs-extra": "^10.1.0",
33
33
  "gitignore": "^0.7.0",
34
- "http": "^0.0.1-security",
35
34
  "inquirer": "^9.1.1",
36
35
  "listr": "^0.14.3",
37
36
  "mini-css-extract-plugin": "2.6.1",
package/src/auth.js CHANGED
@@ -3,8 +3,10 @@
3
3
 
4
4
  import fetch from 'node-fetch';
5
5
  import open from 'open';
6
- import crypto from 'crypto';
7
- import http from 'http';
6
+ import crypto from 'node:crypto';
7
+ import https from 'node:https';
8
+ import http from 'node:http';
9
+ import fs from 'node:fs';
8
10
 
9
11
 
10
12
  class PegaAuth {
@@ -277,12 +279,17 @@ class PegaAuth {
277
279
  } else {
278
280
  if (this.isNode) {
279
281
  // Determine port to listen to by extracting it from redirect uri
280
- const { redirectUri } = this.config;
282
+ const { redirectUri, cert, key } = this.config;
283
+ const isHttp = redirectUri.startsWith('http:');
281
284
  const nLocalhost = redirectUri.indexOf("localhost:");
282
285
  const nSlash = redirectUri.indexOf('/', nLocalhost + 10);
283
286
  const nPort = parseInt(redirectUri.substring(nLocalhost + 10, nSlash));
284
287
  if (-1 !== nLocalhost) {
285
- const server = http.createServer((req, res) => {
288
+ const options = isHttp ? {} : {
289
+ key: fs.readFileSync(key),
290
+ cert: fs.readFileSync(cert)
291
+ };
292
+ const server = (isHttp ? http : https).createServer(options, (req, res) => {
286
293
  const { method, url } = req;
287
294
  const { winTitle, winBodyHtml } = this.config;
288
295
  // console.log(`HTTP request received: ${url}`);
@@ -331,7 +338,7 @@ class PegaAuth {
331
338
  this.#reloadConfig();
332
339
 
333
340
  const { serverType, isolationId, clientId, clientSecret, redirectUri, tokenUri, codeVerifier, grantType,
334
- sessionIndex, customTokenParams, userIdentifier, password, noPKCE } = this.config;
341
+ sessionIndex, customTokenParams, userIdentifier, password, noPKCE, ignoreInvalidCerts } = this.config;
335
342
 
336
343
  const bAuthCode = !grantType || grantType === 'authCode';
337
344
  if (bAuthCode && !authCode && !this.isNode) {
@@ -378,6 +385,7 @@ class PegaAuth {
378
385
  // console.log(`getToken form data: ${formData.toString()}`);
379
386
 
380
387
  return fetch(tokenUri, {
388
+ agent: this.isNode && ignoreInvalidCerts ? new https.Agent({ rejectUnauthorized: false }) : undefined,
381
389
  method: 'POST',
382
390
  headers: new Headers({
383
391
  'content-type': 'application/x-www-form-urlencoded'
@@ -418,7 +426,7 @@ class PegaAuth {
418
426
 
419
427
  /* eslint-disable camelcase */
420
428
  async refreshToken(refresh_token) {
421
- const { clientId, clientSecret, tokenUri } = this.config;
429
+ const { clientId, clientSecret, tokenUri, ignoreInvalidCerts } = this.config;
422
430
 
423
431
  const formData = new URLSearchParams();
424
432
  formData.append('client_id', clientId);
@@ -429,6 +437,7 @@ class PegaAuth {
429
437
  formData.append('refresh_token', refresh_token);
430
438
 
431
439
  return fetch(tokenUri, {
440
+ agent: this.isNode && ignoreInvalidCerts ? new https.Agent({ rejectUnauthorized: false }) : undefined,
432
441
  method: 'POST',
433
442
  headers: new Headers({
434
443
  'content-type': 'application/x-www-form-urlencoded'
@@ -460,7 +469,7 @@ class PegaAuth {
460
469
  // Must have a config structure to proceed
461
470
  return;
462
471
  }
463
- const { clientId, clientSecret, revokeUri } = this.config;
472
+ const { clientId, clientSecret, revokeUri, ignoreInvalidCerts } = this.config;
464
473
 
465
474
  let hdrs = { 'content-type': 'application/x-www-form-urlencoded' };
466
475
  if (clientSecret) {
@@ -478,6 +487,7 @@ class PegaAuth {
478
487
  formData.append('token', prop === 'access_token' ? access_token : refresh_token);
479
488
  formData.append('token_type_hint', prop);
480
489
  fetch(revokeUri, {
490
+ agent: this.isNode && ignoreInvalidCerts ? new https.Agent({ rejectUnauthorized: false }) : undefined,
481
491
  method: 'POST',
482
492
  headers: new Headers(hdrs),
483
493
  body: formData.toString()
@@ -3,8 +3,29 @@ import { getPegaServerConfig } from '../../util.js';
3
3
 
4
4
  import { AUTHENTICATE_SCHEMA } from '../../constants.js';
5
5
 
6
- export const getAuthQuestions = async () => {
7
- const defaultPegaConfig = await getPegaServerConfig();
6
+ export const getAuthQuestions = async (dxcbConfig) => {
7
+ const defaultPegaConfig = dxcbConfig;
8
+ const aMustAsk = dxcbConfig['questions_askAlways'] ? dxcbConfig['questions_askAlways'].split(',') : [];
9
+ const aNeverAsk = dxcbConfig['questions_askNever'] ? dxcbConfig['questions_askNever'].split(',') : [];
10
+
11
+ const initAnswers = (answers) => {
12
+ // Setup default answers (if they are present and non-empty)
13
+ answers.serverType = defaultPegaConfig.serverType || "infinity";
14
+ answers.server = defaultPegaConfig.server || undefined;
15
+ answers.isolationId = defaultPegaConfig.isolationId || undefined;
16
+ answers.clientId = defaultPegaConfig.clientId || undefined;
17
+ answers.grantType = defaultPegaConfig.grantType || "authCode";
18
+ answers.clientSecret = defaultPegaConfig.clientSecret || undefined;
19
+ answers.user = defaultPegaConfig.user || undefined;
20
+ answers.password = defaultPegaConfig.password || undefined;
21
+ };
22
+
23
+ const fnStandardWhen = (prop, answers) => {
24
+ if (aNeverAsk.includes(prop)) {
25
+ return false;
26
+ }
27
+ return aMustAsk.includes(prop) || !answers[prop];
28
+ };
8
29
 
9
30
  return [
10
31
  {
@@ -22,26 +43,24 @@ export const getAuthQuestions = async () => {
22
43
  return 'Pega server type cannot be empty';
23
44
  },
24
45
  when: answers => {
25
- if (!defaultPegaConfig.serverType) {
26
- answers.serverType = "infinity";
27
- }
28
- else if (!defaultPegaConfig.askServerType) {
29
- answers.serverType = defaultPegaConfig.serverType;
30
- }
31
- return (defaultPegaConfig.askServerType ? true : false);
46
+ initAnswers(answers);
47
+ return fnStandardWhen('serverType', answers);
32
48
  }
33
49
  },
34
50
  {
35
51
  name: 'server',
36
52
  message: 'Enter pega server url',
37
- default: defaultPegaConfig.server
53
+ default: defaultPegaConfig.server,
54
+ when: answers => {
55
+ return fnStandardWhen('server', answers);
56
+ }
38
57
  },
39
58
  {
40
59
  name: 'isolationId',
41
60
  message: 'Enter isolation id',
42
61
  default: defaultPegaConfig.isolationId,
43
62
  when: answers => {
44
- return answers.serverType === "launchpad";
63
+ return answers.serverType === "launchpad" && fnStandardWhen('isolationId', answers);
45
64
  }
46
65
  },
47
66
  {
@@ -57,6 +76,9 @@ export const getAuthQuestions = async () => {
57
76
  return true;
58
77
  }
59
78
  return 'Grant type cannot be empty';
79
+ },
80
+ when: answers => {
81
+ return fnStandardWhen('grantType', answers);
60
82
  }
61
83
  },
62
84
  {
@@ -70,7 +92,7 @@ export const getAuthQuestions = async () => {
70
92
  return 'Username cannot be empty';
71
93
  },
72
94
  when: answers => {
73
- return (answers.serverType === "infinity" && answers.grantType !== "clientCreds");
95
+ return (answers.serverType === "infinity" && answers.grantType === "passwordCreds") && fnStandardWhen('user', answers);
74
96
  }
75
97
  },
76
98
  {
@@ -85,7 +107,7 @@ export const getAuthQuestions = async () => {
85
107
  return 'Password cannot be empty';
86
108
  },
87
109
  when: answers => {
88
- return (answers.user && (answers.serverType === "infinity" && answers.grantType !== "clientCreds"));
110
+ return (answers.user && (answers.serverType === "infinity" && answers.grantType === "passwordCreds") && fnStandardWhen('password', answers));
89
111
  }
90
112
  },
91
113
  {
@@ -97,6 +119,9 @@ export const getAuthQuestions = async () => {
97
119
  return true;
98
120
  }
99
121
  return 'clientId cannot be empty';
122
+ },
123
+ when: answers => {
124
+ return fnStandardWhen('clientId', answers);
100
125
  }
101
126
  },
102
127
  {
@@ -111,7 +136,7 @@ export const getAuthQuestions = async () => {
111
136
  return 'clientSecret cannot be empty';
112
137
  },
113
138
  when: answers => {
114
- return (answers.grantType !== "authCode");
139
+ return (answers.grantType !== "authCode" && fnStandardWhen('clientSecret', answers));
115
140
  }
116
141
  }
117
142
  ];
@@ -26,16 +26,24 @@ const generateAccessToken = async config => {
26
26
  const sAuthorizeUri = `${server}${serverType === 'infinity' ? INF_AUTHORIZE_URL_V1 : LP_AUTHORIZE_URL}`;
27
27
  // console.log(`grantType: ${config.grantType}`);
28
28
 
29
+ const authCodeConfig = grantType === 'authCode' ? {
30
+ authorizeUri: sAuthorizeUri,
31
+ redirectUri: config.redirectUri.endsWith('/') ? config.redirectUri : `${config.redirectUri}/`,
32
+ cert: config.cert,
33
+ key: config.key,
34
+ winTitle: 'DX Component Dev',
35
+ winBodyHtml: `Authentication is complete. This browser window may be closed.`
36
+ } : {};
37
+
29
38
  // Build auth config structure
30
39
  const authConfig = {
31
40
  serverType: serverType,
32
41
  grantType: grantType,
33
- authorizeUri: sAuthorizeUri,
34
42
  tokenUri: sTokenUri,
35
43
  clientId: clientId,
36
44
  clientSecret: clientSecret,
37
- winTitle: 'DXCB',
38
- winBodyHtml: `Authentication is complete. This browser window may be closed.`
45
+ ignoreInvalidCerts: true,
46
+ ...authCodeConfig
39
47
  };
40
48
  if (serverType === 'infinity') {
41
49
  authConfig.authService = 'pega';
@@ -48,11 +56,8 @@ const generateAccessToken = async config => {
48
56
  authConfig.isolationId = isolationId;
49
57
  // authConfig.noPKCE = true;
50
58
  }
51
- if (grantType === 'authCode') {
52
- authConfig.redirectUri = 'http://localhost:4010/';
53
- }
54
59
  const auth = new PegaAuth(authConfig);
55
- // console.log(`...about to infoke auth.login\n${JSON.stringify(authConfig)}`);
60
+ // console.log(`...about to invoke auth.login\n${JSON.stringify(authConfig)}`);
56
61
 
57
62
  auth.login()
58
63
  .then((token) => {
@@ -98,30 +103,37 @@ export default async options => {
98
103
  }
99
104
 
100
105
  const serverConfig = data['server-config'];
101
- const params = options.params;
106
+ const isPwdNonEmpty = serverConfig['password'] ? true : false;
102
107
 
103
- const questions = await getAuthQuestions(params);
108
+ // console.log(`serverConfig: ${JSON.stringify(serverConfig)}`);
109
+ const questions = await getAuthQuestions(serverConfig);
110
+ // console.log(`questions: ${JSON.stringify(questions)}`);
104
111
  const answers = await inquirer.prompt(questions);
105
- serverConfig.serverType = answers.serverType;
106
- serverConfig.server = answers.server;
107
- // Isolation id only needed for launchpad
108
- if (answers.serverType === 'launchpad') {
109
- serverConfig.isolationId = answers.isolationId;
110
- }
111
- serverConfig.grantType = answers.grantType;
112
- if (answers.serverType === 'infinity' && answers.user && answers.password) {
113
- serverConfig.user = answers.user;
114
- serverConfig.password = answers.password;
112
+ const aProps = ['serverType', 'server', 'isolationId', 'clientId', 'grantType', 'clientSecret', 'user', 'password'];
113
+ const isLaunchPad = answers.serverType === "launchpad";
114
+ let bWriteUpdate = false;
115
+ for (let i = 0; i < aProps.length; i += 1) {
116
+ const prop = aProps[i];
117
+ if (serverConfig[prop] !== answers[prop]) {
118
+ // console.log(`property ${prop} changed from ${serverConfig[prop]} to ${answers[prop]}`);
119
+ serverConfig[prop] = answers[prop];
120
+ if (prop !== 'password' || isPwdNonEmpty) {
121
+ bWriteUpdate = true;
122
+ }
123
+ }
115
124
  }
116
- serverConfig.clientId = answers.clientId;
117
- serverConfig.clientSecret = answers.clientSecret;
118
125
 
119
126
  try {
127
+ // console.log(`serverConfig: ${JSON.stringify(serverConfig)}`);
120
128
  authOK = await generateAccessToken(serverConfig);
121
129
  } catch (e) {
122
130
  }
123
131
 
124
- if (authOK) {
132
+ if (authOK & bWriteUpdate) {
133
+ // Don't write password value (unless it already had a value)
134
+ if (!isPwdNonEmpty) {
135
+ serverConfig['password'] = '';
136
+ }
125
137
  console.log(`${chalk.green('Writing updated config...')}`);
126
138
  let updCfg = null;
127
139
  try {
@@ -54,7 +54,7 @@
54
54
  },
55
55
  "devDependencies": {
56
56
  "@pega/configs": "^0.3.4",
57
- "@pega/custom-dx-components": "^2.0.23",
57
+ "@pega/custom-dx-components": "^2.0.24",
58
58
  "@pega/eslint-config": "^0.3.2",
59
59
  "@storybook/addon-actions": "^6.5.12",
60
60
  "@storybook/addon-essentials": "^6.5.12",
@@ -1,17 +1,26 @@
1
1
  {
2
2
  "components-directory-path": "src/components",
3
3
  "server-config": {
4
- "serverType": "infinity",
5
- "server": "https://localhost:38764/prweb",
6
- "grantType": "authCode",
7
- "user": "",
8
- "password": "",
9
4
  "rulesetName": "rulesetName",
10
5
  "rulesetVersion": "rulesetVersion",
11
6
  "sourceOfComponents": "Server",
12
7
  "devBuild": false,
8
+ "serverType": "infinity",
9
+ "server": "https://localhost:38764/prweb",
13
10
  "clientId": "",
14
- "clientSecret": ""
11
+ "clientSecret_comment": "Needed only when using 'passwordCreds' or 'clientCreds' grantType",
12
+ "clientSecret": "",
13
+ "grantType": "authCode",
14
+ "redirectUri": "https://localhost:4010/",
15
+ "cert_and_key_comment": "Path to SSL certificate file and SSL key file to use when starting local HTTPS server",
16
+ "cert_and_key_comment2": "Needed only when using 'authCode' grantType and redirectUri begins with 'https://localhost'",
17
+ "cert": "",
18
+ "key": "",
19
+ "user_and_password_comment": "useful primarily when grantType is set to 'passwordCreds'",
20
+ "user": "",
21
+ "password": "",
22
+ "questions_askAlways": "",
23
+ "questions_askNever": ""
15
24
  },
16
25
  "component": {
17
26
  "library": "",