powerbi-visuals-tools 5.4.3 → 5.5.1
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/Changelog.md +8 -0
- package/config.json +5 -3
- package/lib/CertificateTools.js +142 -285
- package/lib/LintValidator.js +3 -3
- package/lib/features/TotalSubTotal.js +1 -1
- package/lib/utils.js +1 -4
- package/lib/webpack.config.js +1 -0
- package/package.json +8 -8
- package/certs/PowerBICustomVisualTest_private.key +0 -28
- package/certs/PowerBICustomVisualTest_public.crt +0 -19
package/Changelog.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
This page contains information about changes to the PowerBI Visual Tools (pbiviz).
|
|
4
4
|
|
|
5
|
+
## 5.5.1
|
|
6
|
+
* Fixed subtotal feature check
|
|
7
|
+
|
|
8
|
+
## 5.5.0
|
|
9
|
+
* Changed path for storing certificates. It allows the certificate to be reused regardless of tools version. New path is `({home directory}/pbiviz-certs)`.
|
|
10
|
+
* Windows version lower 10 is deprecated.
|
|
11
|
+
* Resolve of symlinks is disabled.
|
|
12
|
+
|
|
5
13
|
## 5.4.3
|
|
6
14
|
* Fixed bug with missing plugins for Eslint.
|
|
7
15
|
* New flag `--use-default` for `pbiviz package` and `pbiviz lint` commands. Use this command to lint files according to recommended lint config.
|
package/config.json
CHANGED
|
@@ -17,9 +17,11 @@
|
|
|
17
17
|
"package": { "dropFolder": "dist" },
|
|
18
18
|
"server": {
|
|
19
19
|
"assetsRoute": "/assets",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
20
|
+
"certificateFolder": "pbiviz-certs",
|
|
21
|
+
"privateKey": "PowerBICustomVisualTest_private.key",
|
|
22
|
+
"certificate": "PowerBICustomVisualTest_public.crt",
|
|
23
|
+
"pfx": "PowerBICustomVisualTest_public.pfx",
|
|
24
|
+
"passphrase": "PowerBICustomVisualTestPass.txt"
|
|
23
25
|
},
|
|
24
26
|
"visualTemplates": {
|
|
25
27
|
"circlecard": "https://codeload.github.com/microsoft/powerbi-visuals-circlecard-react/zip/master"
|
package/lib/CertificateTools.js
CHANGED
|
@@ -30,285 +30,181 @@ import fs from 'fs-extra';
|
|
|
30
30
|
import os from 'os';
|
|
31
31
|
import path from 'path';
|
|
32
32
|
import crypto from "crypto";
|
|
33
|
-
import {
|
|
33
|
+
import { readJsonFromRoot } from './utils.js';
|
|
34
34
|
import ConsoleWriter from './ConsoleWriter.js';
|
|
35
35
|
const certSafePeriod = 1000 * 60 * 60 * 24; // 24 hours
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
36
|
+
const config = readJsonFromRoot('config.json');
|
|
37
|
+
const pathToCertFolder = path.join(os.homedir(), config.server.certificateFolder);
|
|
38
|
+
const secretFilesPath = {
|
|
39
|
+
certPath: path.join(pathToCertFolder, config.server.certificate),
|
|
40
|
+
keyPath: path.join(pathToCertFolder, config.server.privateKey),
|
|
41
|
+
pfxPath: path.join(pathToCertFolder, config.server.pfx),
|
|
42
|
+
passphrasePath: path.join(pathToCertFolder, config.server.passphrase)
|
|
43
|
+
};
|
|
44
|
+
function exec(command, options, callback) {
|
|
39
45
|
return new Promise((resolve, reject) => {
|
|
40
|
-
|
|
46
|
+
const defaultCallback = (err, stdout, stderr) => {
|
|
41
47
|
if (err) {
|
|
42
48
|
reject(stderr);
|
|
43
49
|
}
|
|
44
50
|
resolve(stdout);
|
|
45
|
-
}
|
|
51
|
+
};
|
|
52
|
+
nodeExec(command, options, callback ? callback : defaultCallback);
|
|
46
53
|
});
|
|
47
54
|
}
|
|
48
55
|
export async function createCertificate() {
|
|
49
|
-
const
|
|
50
|
-
const certPath = await getCertFile(config, true);
|
|
56
|
+
const certPath = await getCertFile(true);
|
|
51
57
|
if (!certPath) {
|
|
52
58
|
ConsoleWriter.error("Certificate not found. The new certificate will be generated");
|
|
53
|
-
await createCertFile(
|
|
59
|
+
await createCertFile(true);
|
|
54
60
|
}
|
|
55
61
|
else {
|
|
56
|
-
await openCertFile(
|
|
62
|
+
await openCertFile();
|
|
57
63
|
}
|
|
58
64
|
}
|
|
59
|
-
export async function createCertFile(
|
|
65
|
+
export async function createCertFile(open = false) {
|
|
60
66
|
ConsoleWriter.info(`Generating a new certificate...`);
|
|
61
67
|
const subject = "localhost";
|
|
62
68
|
const keyLength = 2048;
|
|
63
|
-
const algorithm = "sha256";
|
|
64
69
|
const validPeriod = 365;
|
|
65
|
-
|
|
66
|
-
open = false;
|
|
67
|
-
}
|
|
68
|
-
const certPath = path.join(rootPath, config.server.certificate);
|
|
69
|
-
const keyPath = path.join(rootPath, config.server.privateKey);
|
|
70
|
-
const pfxPath = path.join(rootPath, config.server.pfx);
|
|
70
|
+
const { certPath, keyPath, pfxPath } = secretFilesPath;
|
|
71
71
|
const openCmds = {
|
|
72
72
|
linux: 'openssl',
|
|
73
73
|
darwin: 'openssl',
|
|
74
|
-
win32: '
|
|
74
|
+
win32: 'pwsh'
|
|
75
75
|
};
|
|
76
76
|
let startCmd = openCmds[os.platform()];
|
|
77
|
-
if (startCmd) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
` -keyout ${keyPath}` +
|
|
105
|
-
` -x509 ` +
|
|
106
|
-
` -days ${validPeriod} ` +
|
|
107
|
-
` -out ${certPath} ` +
|
|
108
|
-
` -subj "/CN=${subject}"`;
|
|
109
|
-
await exec(`${startCmd} ${createCertCommand}`);
|
|
110
|
-
if (await fs.exists(certPath)) {
|
|
111
|
-
ConsoleWriter.info(`Certificate generated. Location is ${certPath}`);
|
|
112
|
-
if (open) {
|
|
113
|
-
await openCertFile(config);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
break;
|
|
117
|
-
case "win32":
|
|
118
|
-
// for windows 7 and others
|
|
119
|
-
// 6.1 - Windows 7
|
|
120
|
-
const osVersion = os.release().split(".");
|
|
121
|
-
if ((Number(osVersion[0]) === 6 && Number(osVersion[1]) === 1) || Number(osVersion[0]) < 6) {
|
|
122
|
-
await removeCertFiles(certPath, keyPath, pfxPath);
|
|
123
|
-
startCmd = "openssl";
|
|
124
|
-
createCertCommand =
|
|
125
|
-
` req -newkey rsa:${keyLength}` +
|
|
126
|
-
` -nodes` +
|
|
127
|
-
` -keyout ${keyPath}` +
|
|
128
|
-
` -x509 ` +
|
|
129
|
-
` -days ${validPeriod} ` +
|
|
130
|
-
` -out ${certPath} ` +
|
|
131
|
-
` -subj "/CN=${subject}"`;
|
|
132
|
-
await exec(`${startCmd} ${createCertCommand}`);
|
|
133
|
-
if (await fs.exists(certPath)) {
|
|
134
|
-
ConsoleWriter.info(`Certificate generated. Location is ${certPath}`);
|
|
135
|
-
if (open) {
|
|
136
|
-
await openCertFile(config);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
let randomValues;
|
|
142
|
-
if (crypto.getRandomValues !== undefined) {
|
|
143
|
-
randomValues = crypto.getRandomValues(new Uint32Array(1));
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
randomValues = crypto.webcrypto.getRandomValues(new Uint32Array(1));
|
|
147
|
-
}
|
|
148
|
-
const passphrase = randomValues[0].toString().substring(2);
|
|
149
|
-
config.server.passphrase = passphrase;
|
|
150
|
-
fs.writeFileSync(path.join(rootPath, confPath), JSON.stringify(config));
|
|
151
|
-
// for windows 8 / 8.1 / server 2012 R2 /
|
|
152
|
-
if (Number(osVersion[0]) === 6 && (Number(osVersion[1]) === 2 || Number(osVersion[1]) === 3)) {
|
|
153
|
-
// for 10
|
|
154
|
-
createCertCommand = `$cert = ('Cert:\\CurrentUser\\My\\' + (` +
|
|
155
|
-
` New-SelfSignedCertificate ` +
|
|
156
|
-
` -DnsName localhost ` +
|
|
157
|
-
` -CertStoreLocation Cert:\\CurrentUser\\My ` +
|
|
158
|
-
` | select Thumbprint | ` +
|
|
159
|
-
` ForEach-Object { $_.Thumbprint.ToString() }).toString()); ` +
|
|
160
|
-
` Export-PfxCertificate -Cert $cert` +
|
|
161
|
-
` -FilePath '${pfxPath}' ` +
|
|
162
|
-
` -Password (ConvertTo-SecureString -String '${passphrase}' -Force -AsPlainText)`;
|
|
163
|
-
await exec(`${startCmd} "${createCertCommand}"`);
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
// for window 10 / server 2016
|
|
167
|
-
createCertCommand = `$cert = ('Cert:\\CurrentUser\\My\\' + (` +
|
|
168
|
-
` New-SelfSignedCertificate ` +
|
|
169
|
-
` -DnsName localhost ` +
|
|
170
|
-
` -HashAlgorithm ${algorithm} ` +
|
|
171
|
-
` -Type Custom ` +
|
|
172
|
-
` -Subject ${subject} ` +
|
|
173
|
-
` -KeyAlgorithm RSA ` +
|
|
174
|
-
` -KeyLength ${keyLength} ` +
|
|
175
|
-
` -KeyExportPolicy Exportable ` +
|
|
176
|
-
` -CertStoreLocation Cert:\\CurrentUser\\My ` +
|
|
177
|
-
` -NotAfter (get-date).AddDays(${validPeriod}) ` +
|
|
178
|
-
` | select Thumbprint | ` +
|
|
179
|
-
` ForEach-Object { $_.Thumbprint.ToString() }).toString()); ` +
|
|
180
|
-
` Export-PfxCertificate -Cert $cert` +
|
|
181
|
-
` -FilePath '${pfxPath}' ` +
|
|
182
|
-
` -Password (ConvertTo-SecureString -String '${passphrase}' -Force -AsPlainText)`;
|
|
183
|
-
await exec(`${startCmd} "${createCertCommand}"`);
|
|
184
|
-
}
|
|
185
|
-
if (await fs.exists(pfxPath)) {
|
|
186
|
-
ConsoleWriter.info(`Certificate generated. Location is ${pfxPath}. Passphrase is '${passphrase}'`);
|
|
77
|
+
if (!startCmd) {
|
|
78
|
+
ConsoleWriter.error(['Unknown platform. Please place a custom-generated certificate in:', certPath]);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
let createCertCommand = "";
|
|
83
|
+
let passphrase = "";
|
|
84
|
+
fs.ensureDirSync(path.dirname(certPath));
|
|
85
|
+
switch (os.platform()) {
|
|
86
|
+
case "linux":
|
|
87
|
+
case "darwin":
|
|
88
|
+
createCertCommand =
|
|
89
|
+
` req -newkey rsa:${keyLength}` +
|
|
90
|
+
` -nodes` +
|
|
91
|
+
` -keyout ${keyPath}` +
|
|
92
|
+
` -x509 ` +
|
|
93
|
+
` -days ${validPeriod} ` +
|
|
94
|
+
` -out ${certPath} ` +
|
|
95
|
+
` -subj "/CN=${subject}"`;
|
|
96
|
+
await Promise.all([
|
|
97
|
+
removeCertFiles(certPath, keyPath),
|
|
98
|
+
exec(`${startCmd} ${createCertCommand}`)
|
|
99
|
+
]);
|
|
100
|
+
if (await fs.exists(certPath)) {
|
|
101
|
+
ConsoleWriter.info(`Certificate generated. Location is ${certPath}`);
|
|
102
|
+
if (open) {
|
|
103
|
+
await openCertFile();
|
|
187
104
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
105
|
+
}
|
|
106
|
+
break;
|
|
107
|
+
case "win32":
|
|
108
|
+
passphrase = getRandomValues()[0].toString().substring(2);
|
|
109
|
+
createCertCommand = `$cert = ('Cert:\\CurrentUser\\My\\' + (` +
|
|
110
|
+
` New-SelfSignedCertificate ` +
|
|
111
|
+
` -DnsName localhost ` +
|
|
112
|
+
` -Type Custom ` +
|
|
113
|
+
` -Subject 'CN=${subject}' ` +
|
|
114
|
+
` -KeyAlgorithm RSA ` +
|
|
115
|
+
` -KeyLength ${keyLength} ` +
|
|
116
|
+
` -KeyExportPolicy Exportable ` +
|
|
117
|
+
` -CertStoreLocation Cert:\\CurrentUser\\My ` +
|
|
118
|
+
` -NotAfter (get-date).AddDays(${validPeriod}) ` +
|
|
119
|
+
` | select Thumbprint | ` +
|
|
120
|
+
` ForEach-Object { $_.Thumbprint.ToString() }).toString()); ` +
|
|
121
|
+
` Export-PfxCertificate -Cert $cert` +
|
|
122
|
+
` -FilePath '${pfxPath}' ` +
|
|
123
|
+
` -Password (ConvertTo-SecureString -String '${passphrase}' -Force -AsPlainText)`;
|
|
124
|
+
await Promise.all([
|
|
125
|
+
removeCertFiles(certPath, keyPath, pfxPath),
|
|
126
|
+
exec(`${startCmd} -Command "${createCertCommand}"`),
|
|
127
|
+
savePassphrase(passphrase)
|
|
128
|
+
]);
|
|
129
|
+
if (await fs.exists(pfxPath)) {
|
|
130
|
+
ConsoleWriter.info(`Certificate generated. Location is ${pfxPath}. Passphrase is ${passphrase}`);
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
default:
|
|
134
|
+
ConsoleWriter.error('Unknown platform');
|
|
135
|
+
break;
|
|
205
136
|
}
|
|
206
137
|
}
|
|
207
|
-
|
|
208
|
-
|
|
138
|
+
catch (e) {
|
|
139
|
+
if (e && e.message && e.message.indexOf("'openssl' is not recognized as an internal or external command") > 0) {
|
|
140
|
+
ConsoleWriter.warning('Create certificate error:');
|
|
141
|
+
ConsoleWriter.warning('OpenSSL is not installed or not available from command line');
|
|
142
|
+
ConsoleWriter.info('Install OpenSSL from https://www.openssl.org or https://wiki.openssl.org/index.php/Binaries');
|
|
143
|
+
ConsoleWriter.info('and try again');
|
|
144
|
+
ConsoleWriter.info('Read more at');
|
|
145
|
+
ConsoleWriter.info('https://github.com/Microsoft/PowerBI-visuals/blob/master/tools/CreateCertificate.md#manual');
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
ConsoleWriter.error(['Create certificate error:', e]);
|
|
149
|
+
}
|
|
209
150
|
}
|
|
210
151
|
}
|
|
211
|
-
async function getCertFile(
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const cert = path.join(rootPath, config.server.certificate);
|
|
216
|
-
const pfx = path.join(rootPath, config.server.pfx);
|
|
217
|
-
if (await fs.exists(cert)) {
|
|
218
|
-
return cert;
|
|
152
|
+
async function getCertFile(silent = false) {
|
|
153
|
+
const { certPath, pfxPath, passphrasePath } = secretFilesPath;
|
|
154
|
+
if (await fs.exists(certPath)) {
|
|
155
|
+
return certPath;
|
|
219
156
|
}
|
|
220
|
-
if (await fs.exists(
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
157
|
+
if (await fs.exists(pfxPath)) {
|
|
158
|
+
if (!silent && await fs.exists(passphrasePath)) {
|
|
159
|
+
const passphrase = await fs.readFile(passphrasePath, 'utf8');
|
|
160
|
+
ConsoleWriter.info(`Use '${passphrase}' passphrase to install PFX certificate.`);
|
|
225
161
|
}
|
|
226
|
-
return
|
|
162
|
+
return pfxPath;
|
|
227
163
|
}
|
|
228
164
|
if (!silent) {
|
|
229
165
|
ConsoleWriter.info('Certificate not found. Call `pbiviz install-cert` command to create the new certificate');
|
|
230
166
|
}
|
|
231
167
|
return null;
|
|
232
168
|
}
|
|
233
|
-
async function openCertFile(
|
|
234
|
-
const certPath = await getCertFile(config);
|
|
235
|
-
if (!certPath) {
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
169
|
+
async function openCertFile() {
|
|
238
170
|
const openCmds = {
|
|
239
171
|
linux: 'xdg-open',
|
|
240
172
|
darwin: 'open',
|
|
241
173
|
win32: 'powershell start'
|
|
242
174
|
};
|
|
243
175
|
const startCmd = openCmds[os.platform()];
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
catch (e) {
|
|
249
|
-
ConsoleWriter.info(['Certificate path:', certPath]);
|
|
176
|
+
const certPath = await getCertFile();
|
|
177
|
+
try {
|
|
178
|
+
if (!startCmd || !certPath) {
|
|
179
|
+
throw new Error();
|
|
250
180
|
}
|
|
181
|
+
await exec(`${startCmd} "${certPath}"`);
|
|
251
182
|
}
|
|
252
|
-
|
|
183
|
+
catch (e) {
|
|
253
184
|
ConsoleWriter.info(['Certificate path:', certPath]);
|
|
254
185
|
}
|
|
255
186
|
}
|
|
256
|
-
export async function removeCertFiles(
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
catch (e) {
|
|
261
|
-
if (!e.message.indexOf("no such file or directory")) {
|
|
262
|
-
throw e;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
try {
|
|
266
|
-
await fs.unlink(keyPath);
|
|
267
|
-
}
|
|
268
|
-
catch (e) {
|
|
269
|
-
if (!e.message.indexOf("no such file or directory")) {
|
|
270
|
-
throw e;
|
|
187
|
+
export async function removeCertFiles(...paths) {
|
|
188
|
+
paths.forEach(async (path) => {
|
|
189
|
+
try {
|
|
190
|
+
await fs.unlink(path);
|
|
271
191
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
catch (e) {
|
|
277
|
-
if (!e.message.indexOf("no such file or directory")) {
|
|
278
|
-
throw e;
|
|
192
|
+
catch (e) {
|
|
193
|
+
if (!e.message.indexOf("no such file or directory")) {
|
|
194
|
+
throw e;
|
|
195
|
+
}
|
|
279
196
|
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
async function getGlobalPbivizCerts(config) {
|
|
283
|
-
const options = {};
|
|
284
|
-
try {
|
|
285
|
-
const location = (await exec('npm ls -g powerbi-visuals-tools')).split("\n")[0];
|
|
286
|
-
const certPath = path.join(location, "node_modules", "powerbi-visuals-tools", config.server.certificate);
|
|
287
|
-
const keyPath = path.join(location, "node_modules", "powerbi-visuals-tools", config.server.privateKey);
|
|
288
|
-
const pfxPath = path.join(location, "node_modules", "powerbi-visuals-tools", config.server.pfx);
|
|
289
|
-
const globalPbivizConfig = path.join(location, "node_modules", "powerbi-visuals-tools", "config.json");
|
|
290
|
-
options.passphrase = fs.existsSync(globalPbivizConfig) && fs.readJSONSync(globalPbivizConfig).server.passphrase;
|
|
291
|
-
const CertFileVerified = await verifyCertFile(keyPath, certPath, pfxPath, options.passphrase);
|
|
292
|
-
options.cert = fs.existsSync(certPath) && certPath;
|
|
293
|
-
options.key = fs.existsSync(keyPath) && keyPath;
|
|
294
|
-
options.pfx = fs.existsSync(pfxPath) && CertFileVerified && pfxPath;
|
|
295
|
-
}
|
|
296
|
-
catch (err) {
|
|
297
|
-
ConsoleWriter.warning(`Global certificate error: ${err}`);
|
|
298
|
-
}
|
|
299
|
-
if (!options.cert && !options.pfx) {
|
|
300
|
-
ConsoleWriter.warning(`Global instance of valid pbiviz certificate not found.`);
|
|
301
|
-
}
|
|
302
|
-
return options;
|
|
197
|
+
});
|
|
303
198
|
}
|
|
304
199
|
export async function resolveCertificate() {
|
|
305
|
-
const config = readJsonFromRoot('config.json');
|
|
306
200
|
const options = {};
|
|
307
|
-
const keyPath =
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
201
|
+
const { certPath, keyPath, pfxPath, passphrasePath } = secretFilesPath;
|
|
202
|
+
const isCertificateValid = await verifyCertFile(keyPath, certPath, pfxPath, passphrasePath);
|
|
203
|
+
if (!isCertificateValid) {
|
|
204
|
+
await createCertFile();
|
|
205
|
+
}
|
|
206
|
+
if (await fs.exists(passphrasePath)) {
|
|
207
|
+
options.passphrase = await fs.readFile(passphrasePath, 'utf8');
|
|
312
208
|
}
|
|
313
209
|
if (await fs.exists(keyPath)) {
|
|
314
210
|
options.key = await fs.readFile(keyPath);
|
|
@@ -319,72 +215,24 @@ export async function resolveCertificate() {
|
|
|
319
215
|
if (await fs.exists(pfxPath)) {
|
|
320
216
|
options.pfx = await fs.readFile(pfxPath);
|
|
321
217
|
}
|
|
322
|
-
const CertFileVerified = await verifyCertFile(keyPath, certPath, pfxPath, options.passphrase);
|
|
323
|
-
if ((!options.cert && !options.pfx) || !CertFileVerified) {
|
|
324
|
-
ConsoleWriter.warning("Local valid certificate not found.");
|
|
325
|
-
ConsoleWriter.info("Checking global instance of pbiviz certificate...");
|
|
326
|
-
const globalPbivizOptions = await getGlobalPbivizCerts(config);
|
|
327
|
-
if (!globalPbivizOptions.cert && !globalPbivizOptions.pfx) {
|
|
328
|
-
await createCertFile(config, true);
|
|
329
|
-
if (!(await getCertFile(config, true))) {
|
|
330
|
-
ConsoleWriter.error('Certificate wasn\'t created');
|
|
331
|
-
throw new Error("Call `pbiviz install-cert` command to create the new certificate");
|
|
332
|
-
}
|
|
333
|
-
else {
|
|
334
|
-
if (config.server.passphrase) {
|
|
335
|
-
options.passphrase = config.server.passphrase;
|
|
336
|
-
}
|
|
337
|
-
if (await fs.exists(keyPath)) {
|
|
338
|
-
options.key = await fs.readFile(keyPath);
|
|
339
|
-
}
|
|
340
|
-
if (await fs.exists(certPath)) {
|
|
341
|
-
options.cert = await fs.readFile(certPath);
|
|
342
|
-
}
|
|
343
|
-
if (await fs.exists(pfxPath)) {
|
|
344
|
-
options.pfx = await fs.readFile(pfxPath);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
// copy certs to local instance
|
|
350
|
-
ConsoleWriter.info("Copy server certificate from global instance of pbiviz...");
|
|
351
|
-
if (globalPbivizOptions.cert) {
|
|
352
|
-
await fs.copyFile(globalPbivizOptions.cert, path.join(rootPath, config.server.certificate));
|
|
353
|
-
options.certificate = config.server.certificate;
|
|
354
|
-
}
|
|
355
|
-
if (globalPbivizOptions.key) {
|
|
356
|
-
await fs.copyFile(globalPbivizOptions.key, path.join(rootPath, config.server.privateKey));
|
|
357
|
-
options.privateKey = config.server.privateKey;
|
|
358
|
-
}
|
|
359
|
-
if (globalPbivizOptions.pfx) {
|
|
360
|
-
await fs.copyFile(globalPbivizOptions.pfx, path.join(rootPath, config.server.pfx));
|
|
361
|
-
// need to pass whole file instead path to file
|
|
362
|
-
options.pfx = await fs.readFile(path.join(rootPath, config.server.pfx));
|
|
363
|
-
options.passphrase = globalPbivizOptions.passphrase;
|
|
364
|
-
// eslint-disable-next-line require-atomic-updates
|
|
365
|
-
config.server.passphrase = globalPbivizOptions.passphrase;
|
|
366
|
-
}
|
|
367
|
-
await fs.writeFile(path.join(rootPath, confPath), JSON.stringify(config));
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
218
|
return options;
|
|
371
219
|
}
|
|
372
|
-
export async function verifyCertFile(keyPath, certPath, pfxPath,
|
|
373
|
-
let verifyCertDate;
|
|
220
|
+
export async function verifyCertFile(keyPath, certPath, pfxPath, passphrasePath) {
|
|
221
|
+
let verifyCertDate = false;
|
|
374
222
|
try {
|
|
375
223
|
let endDateStr;
|
|
376
|
-
// For Windows OS:
|
|
377
224
|
if (os.platform() === "win32") {
|
|
378
|
-
if (!fs.existsSync(pfxPath) || !
|
|
379
|
-
|
|
225
|
+
if (!fs.existsSync(pfxPath) || !fs.existsSync(passphrasePath)) {
|
|
226
|
+
throw new Error('PFX or passphrase file not found');
|
|
380
227
|
}
|
|
381
|
-
const
|
|
228
|
+
const passphrase = await fs.readFile(passphrasePath, 'utf8');
|
|
229
|
+
const command = `(New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("${pfxPath}",'${passphrase}')).NotAfter.ToString('yyyy-MM-dd HH:mm:ss')`;
|
|
230
|
+
const certStr = await exec(command, { shell: 'powershell.exe' });
|
|
382
231
|
endDateStr = certStr.trim();
|
|
383
232
|
}
|
|
384
|
-
// For Linux and Mac/darwin OS:
|
|
385
233
|
else if (os.platform() === "linux" || os.platform() === "darwin") {
|
|
386
|
-
if (!fs.
|
|
387
|
-
|
|
234
|
+
if (!(await fs.exists(certPath))) {
|
|
235
|
+
throw new Error('Certificate file not found');
|
|
388
236
|
}
|
|
389
237
|
endDateStr = await exec(`openssl x509 -enddate -noout -in ${certPath} | cut -d = -f 2`);
|
|
390
238
|
}
|
|
@@ -394,13 +242,22 @@ export async function verifyCertFile(keyPath, certPath, pfxPath, passphrase) {
|
|
|
394
242
|
ConsoleWriter.info(`Certificate is valid.`);
|
|
395
243
|
}
|
|
396
244
|
else {
|
|
397
|
-
|
|
398
|
-
removeCertFiles(certPath, keyPath, pfxPath);
|
|
245
|
+
throw new Error('Certificate is invalid');
|
|
399
246
|
}
|
|
400
247
|
}
|
|
401
248
|
catch (err) {
|
|
402
249
|
ConsoleWriter.warning(`Certificate verification error: ${err}`);
|
|
403
|
-
removeCertFiles(certPath, keyPath, pfxPath);
|
|
250
|
+
removeCertFiles(certPath, keyPath, pfxPath, passphrasePath);
|
|
404
251
|
}
|
|
405
252
|
return verifyCertDate;
|
|
406
253
|
}
|
|
254
|
+
const getRandomValues = () => {
|
|
255
|
+
if (crypto.getRandomValues !== undefined) {
|
|
256
|
+
return crypto.getRandomValues(new Uint32Array(1));
|
|
257
|
+
}
|
|
258
|
+
return crypto.webcrypto.getRandomValues(new Uint32Array(1));
|
|
259
|
+
};
|
|
260
|
+
const savePassphrase = async (passphrase) => {
|
|
261
|
+
const { passphrasePath } = secretFilesPath;
|
|
262
|
+
await fs.writeFileSync(passphrasePath, passphrase);
|
|
263
|
+
};
|
package/lib/LintValidator.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ESLint } from "eslint";
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import fs from 'fs';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
4
|
import ConsoleWriter from "./ConsoleWriter.js";
|
|
5
|
-
import {
|
|
5
|
+
import { getRootPath } from "./utils.js";
|
|
6
6
|
export class LintValidator {
|
|
7
7
|
visualPath;
|
|
8
8
|
rootPath;
|
|
@@ -60,7 +60,7 @@ export class LintValidator {
|
|
|
60
60
|
resolvePluginsRelativeTo: this.getPluginPath()
|
|
61
61
|
};
|
|
62
62
|
const eslintrcExtensions = ['.json', '.js', '.cjs', '.ts', ''];
|
|
63
|
-
if (!this.useDefault && eslintrcExtensions.some(el =>
|
|
63
|
+
if (!this.useDefault && eslintrcExtensions.some(el => fs.existsSync(path.join(this.visualPath, `.eslintrc${el}`)))) {
|
|
64
64
|
this.config = requiredConfig;
|
|
65
65
|
}
|
|
66
66
|
else {
|
|
@@ -7,6 +7,6 @@ export default class TotalSubTotal {
|
|
|
7
7
|
static visualFeatureType = VisualFeatureType.Matrix;
|
|
8
8
|
static errorMessage = `${this.featureName} - ${this.documentationLink}`;
|
|
9
9
|
static isSupported(packageInstance) {
|
|
10
|
-
return packageInstance.isCapabilityEnabled({ subtotals:
|
|
10
|
+
return packageInstance.isCapabilityEnabled({ subtotals: {} });
|
|
11
11
|
}
|
|
12
12
|
}
|
package/lib/utils.js
CHANGED
|
@@ -22,7 +22,7 @@ export function getRootPath() {
|
|
|
22
22
|
const pathToDirectory = fileURLToPath(import.meta.url);
|
|
23
23
|
return path.join(pathToDirectory, "..", "..");
|
|
24
24
|
}
|
|
25
|
-
|
|
25
|
+
function readFileFromRoot(filePath) {
|
|
26
26
|
return fs.readFileSync(path.join(getRootPath(), filePath), "utf8");
|
|
27
27
|
}
|
|
28
28
|
export function readJsonFromRoot(filePath) {
|
|
@@ -31,6 +31,3 @@ export function readJsonFromRoot(filePath) {
|
|
|
31
31
|
export function readJsonFromVisual(filePath, visualPath) {
|
|
32
32
|
return JSON.parse(fs.readFileSync(path.join(visualPath ?? process.cwd(), filePath), "utf8"));
|
|
33
33
|
}
|
|
34
|
-
export function fileExists(pathToFile, fileName) {
|
|
35
|
-
return fs.existsSync(path.join(pathToFile, fileName));
|
|
36
|
-
}
|
package/lib/webpack.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "powerbi-visuals-tools",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.5.1",
|
|
4
4
|
"description": "Command line tool for creating and publishing visuals for Power BI",
|
|
5
5
|
"main": "./bin/pbiviz.js",
|
|
6
6
|
"type": "module",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://github.com/Microsoft/PowerBI-visuals-tools#readme",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@typescript-eslint/parser": "^
|
|
32
|
+
"@typescript-eslint/parser": "^7.12.0",
|
|
33
33
|
"assert": "^2.1.0",
|
|
34
34
|
"async": "^3.2.5",
|
|
35
35
|
"browserify-zlib": "^0.2.0",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"console-browserify": "^1.2.0",
|
|
41
41
|
"constants-browserify": "^1.0.0",
|
|
42
42
|
"crypto-browserify": "^3.12.0",
|
|
43
|
-
"css-loader": "^6.
|
|
43
|
+
"css-loader": "^6.11.0",
|
|
44
44
|
"domain-browser": "^5.7.0",
|
|
45
45
|
"events": "^3.3.0",
|
|
46
46
|
"extra-watch-webpack-plugin": "^1.0.3",
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"json-loader": "0.5.7",
|
|
51
51
|
"jszip": "^3.10.1",
|
|
52
52
|
"less": "^4.2.0",
|
|
53
|
-
"less-loader": "^
|
|
53
|
+
"less-loader": "^12.2.0",
|
|
54
54
|
"lodash.clonedeep": "4.5.0",
|
|
55
55
|
"lodash.defaults": "4.2.0",
|
|
56
56
|
"lodash.isequal": "4.5.0",
|
|
57
57
|
"lodash.ismatch": "^4.4.0",
|
|
58
|
-
"mini-css-extract-plugin": "^2.
|
|
58
|
+
"mini-css-extract-plugin": "^2.9.0",
|
|
59
59
|
"os-browserify": "^0.3.0",
|
|
60
60
|
"path-browserify": "^1.0.1",
|
|
61
61
|
"powerbi-visuals-webpack-plugin": "4.1.0",
|
|
@@ -75,16 +75,16 @@
|
|
|
75
75
|
"util": "^0.12.5",
|
|
76
76
|
"vm-browserify": "^1.1.2",
|
|
77
77
|
"webpack": "^5.91.0",
|
|
78
|
-
"webpack-bundle-analyzer": "4.10.
|
|
78
|
+
"webpack-bundle-analyzer": "4.10.2",
|
|
79
79
|
"webpack-dev-server": "^4.15.2"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
82
|
+
"@typescript-eslint/eslint-plugin": "^7.12.0",
|
|
83
83
|
"eslint": "^8.57.0",
|
|
84
84
|
"eslint-plugin-powerbi-visuals": "^0.8.1",
|
|
85
85
|
"jasmine": "5.1.0",
|
|
86
86
|
"jasmine-spec-reporter": "7.0.0",
|
|
87
|
-
"semver": "7.6.
|
|
87
|
+
"semver": "7.6.2",
|
|
88
88
|
"tree-kill": "1.2.2",
|
|
89
89
|
"webpack-cli": "^5.1.4"
|
|
90
90
|
},
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
-----BEGIN PRIVATE KEY-----
|
|
2
|
-
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDA/HV0YoD7zmrM
|
|
3
|
-
+IwncAJLHH7ld0deBjC4tIPzv06sGwOqLgnUy5zSjHIb5k7/ojuyW1KQ1dcWU3az
|
|
4
|
-
yj6To9Sl7aieuzHbobsUM7jSKTmCk5zowjZa9JSmU3gP/Ma2/IptvtkWoa8oTCU9
|
|
5
|
-
/iisjLugVBUNyAjSObIWOqtLDlXgHjfX7U/PhrZmQaAC6Z33uHB+J+L3tQq1B7fx
|
|
6
|
-
bNDuW1qQhdTodZ7ecKeyYXZ6uGP961+WyUWFIkLJxYojXYI9QwEfOIMQN3H9LqaP
|
|
7
|
-
m7ZsYtdzUfKcWryRRHiejUALNdkDIpLYixfPsoVzBty2uVE4qFb0vuWA9pVhIdIx
|
|
8
|
-
zAmQbvxnAgMBAAECggEAPzp3wQoE+HtIwp8sTD+GgNV1Pfk2iLe+fsWl9dZYEEtJ
|
|
9
|
-
iBhNYErVtqYNPNbYSyj8JhOFma52KfpuiblZh0XGBhc3h8oNK8hxYs0PYMvg68cx
|
|
10
|
-
QZFf/azkhxoR3CsVcB6NvCcrOR8AJFPOX+FFskLptj3WKA3B1h7RGjdep9iqiiLK
|
|
11
|
-
fxMAJgZE0QYcvAzLcLKYIGvRgVRx9Koq3v+2pRUhcI75e+QSFuXf+NpETVCMlr8i
|
|
12
|
-
o0wJsl1gyCOVpsxv1FgUWFs8BT1mawbKK3z0sJ1FT6sICeGtb3O/cV3QhGieJRS7
|
|
13
|
-
A8p8cCa/4+FXAiXExniABDcZwoft/c0AZh0k4mfKAQKBgQDsG94VmPc8CnKbOXZy
|
|
14
|
-
UkXyPOhp571qMRGR1znVtiZ7nuExSsep7rguE3a8z9ELdhtXDC3Z16UGAfu0B5tO
|
|
15
|
-
tWDjaI+0jauHr/YfMOYNm6MwqaxYgfmUumVhHzxHLu1HNu5NIab1sdrrSUZqPWqa
|
|
16
|
-
DzFOTlw8HUQKEce/EcPjJZ1caQKBgQDRPpG3b1SYOJQZvqdZdWYt6LAwarK5NKbM
|
|
17
|
-
Vfi7ctH6uiCDcE7Kpc0vnvxPIhImFTeBEqKDu4VnEDml6cErIMdOWiVSIPSxvHeb
|
|
18
|
-
wQ6NLybINQpsEkByy2ln7+W+FHLjR5d3ZrTScVtqulVNCxYq/4e+Su0teKkw0kXg
|
|
19
|
-
J7EISFS4TwKBgQDK8jk2v0Dj7ucL3ZyeL3HIAeqT/YmmmX7MqP8hX2w8nhha0jhF
|
|
20
|
-
LNhLYpeRO+AnxeFCNUnEEcPbIiFEYbVxNh6N2faL76oc+3bG4LUdxUXEMlPdz/rD
|
|
21
|
-
iGRT0xFw7jkuGV1EF0T3s2TFETrhXuEegyiScnB9i3pMXXQgHBkQSei04QKBgDFP
|
|
22
|
-
8p1tYl1hcB3FHcFqdoM5RFSUwLv/GZQsaqsaezwAo2r2i8scWgfwCvxHRkmtVGBZ
|
|
23
|
-
2Oz8w4qvezaLqVeVqQLhIcyCMci1ZubRD7HcO+KRBXzErGWhZJz3H4i1XwILPDoK
|
|
24
|
-
fXDfADFd6n52r2nAwOfq1oyR1CXKWGdkU0dlbHnnAoGAGxJ3vRghrEMx3FjUo59S
|
|
25
|
-
sjFZUuTNdyZZFGV5ljlqYT//h4meLqENAXaqZ+shGc5Fkd8vqOO9m2dWyP5fANLo
|
|
26
|
-
Q+ll6+24umDaKOKTJ3uZ1Y1lNjNsf3NyCgljvFp7zUEesCZTP9I0ZpPPb2pPXAHw
|
|
27
|
-
vE2wID0n9rOdFO88j56f6z0=
|
|
28
|
-
-----END PRIVATE KEY-----
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
-----BEGIN CERTIFICATE-----
|
|
2
|
-
MIIDCTCCAfGgAwIBAgIUK/pAQEwJJMgHkQ4n58nNO/cwsNcwDQYJKoZIhvcNAQEL
|
|
3
|
-
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDMyOTE3MDU0OFoXDTI1MDMy
|
|
4
|
-
OTE3MDU0OFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
|
|
5
|
-
AAOCAQ8AMIIBCgKCAQEAwPx1dGKA+85qzPiMJ3ACSxx+5XdHXgYwuLSD879OrBsD
|
|
6
|
-
qi4J1Muc0oxyG+ZO/6I7sltSkNXXFlN2s8o+k6PUpe2onrsx26G7FDO40ik5gpOc
|
|
7
|
-
6MI2WvSUplN4D/zGtvyKbb7ZFqGvKEwlPf4orIy7oFQVDcgI0jmyFjqrSw5V4B43
|
|
8
|
-
1+1Pz4a2ZkGgAumd97hwfifi97UKtQe38WzQ7ltakIXU6HWe3nCnsmF2erhj/etf
|
|
9
|
-
lslFhSJCycWKI12CPUMBHziDEDdx/S6mj5u2bGLXc1HynFq8kUR4no1ACzXZAyKS
|
|
10
|
-
2IsXz7KFcwbctrlROKhW9L7lgPaVYSHSMcwJkG78ZwIDAQABo1MwUTAdBgNVHQ4E
|
|
11
|
-
FgQUNVa/M6yDr6zThWXt51Ag6kZlAaYwHwYDVR0jBBgwFoAUNVa/M6yDr6zThWXt
|
|
12
|
-
51Ag6kZlAaYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAGYjh
|
|
13
|
-
ykXpTW2OcRUM6xPcp9MGdr/b1V1sQQ2OI4GXBuBuqBybY1mROSvprzmEre+SslWI
|
|
14
|
-
joR/wepusgPsoWl54Gylg1eir4xWPq06U6KkNtngMpwHO1ln97Fw6eEND0bJWcn3
|
|
15
|
-
HKagaqOd96vNii4AjMeQnncNUqQGfizDjEOwDY0JY6A4DDjf+Zud6dcpkr7emZby
|
|
16
|
-
3ZQEQ5Jzt2aCqmc1Dzbw2RE/mnlcc87x0jWXyTGVC+L+e08et8WLPFcDtgVa+2L0
|
|
17
|
-
NSVE+P9vchDdX8oHcqGovC0LZibkJZ46oNtqeQQ5/LN2P3Zk6dji4BI7u//U03Gf
|
|
18
|
-
ko5df8qJp7CXHrl6+Q==
|
|
19
|
-
-----END CERTIFICATE-----
|