Skip to content

Commit 0744cc6

Browse files
committed
Impemented trusted.extensions support
Also, Hiera Editor now submits CSR with pp_project = "hiera_editor" so it could be easily authenticated by extensions.pp_project field in the certificate Fixed hierarchy resolving
1 parent 23535a6 commit 0744cc6

File tree

11 files changed

+305
-173
lines changed

11 files changed

+305
-173
lines changed

package-lock.json

Lines changed: 83 additions & 102 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "hiera-editor",
3-
"version": "0.1.3",
3+
"version": "0.1.4",
44
"description": "A GUI tool to manage your Puppet/Hiera for you",
55
"main": "dist/main.js",
66
"scripts": {
@@ -86,6 +86,7 @@
8686
},
8787
"dependencies": {
8888
"@fortawesome/fontawesome-free": "^5.3.1",
89+
"@types/request": "^2.48.1",
8990
"app-root-path": "^2.0.1",
9091
"dialogs": "^1.1.20",
9192
"domain-name-parser": "^2.3.0",
@@ -99,7 +100,9 @@
99100
"ini": "^1.3.5",
100101
"install": "^0.10.2",
101102
"jquery": "^3.2.1",
103+
"node-forge": "^0.7.6",
102104
"npm": "^5.10.0",
105+
"request": "^2.88.0",
103106
"slash": "^2.0.0",
104107
"static-eval": ">=2.0.0",
105108
"text-ellipsis": "^1.0.3",

src/async.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,18 @@ export async function mostRecentFileTime(start: string): Promise<number>
128128
return lastModifiedTime;
129129
}
130130

131+
export function makeDirectories(path: string): Promise<boolean>
132+
{
133+
return new Promise<boolean>((resolve, reject) =>
134+
{
135+
fs.mkdirs(path, (err) =>
136+
{
137+
resolve(err == null);
138+
})
139+
});
140+
}
141+
142+
131143
export function makeDirectory(path: string): Promise<boolean>
132144
{
133145
return new Promise<boolean>((resolve, reject) =>

src/puppet/cert.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
export const CERTIFICATE_EXTENSIONS: {[key: string]: string} = {
3+
"1.3.6.1.4.1.34380.1.1.1": "pp_uuid",
4+
"1.3.6.1.4.1.34380.1.1.2": "pp_instance_id",
5+
"1.3.6.1.4.1.34380.1.1.3": "pp_image_name",
6+
"1.3.6.1.4.1.34380.1.1.4": "pp_preshared_key",
7+
"1.3.6.1.4.1.34380.1.1.5": "pp_cost_center",
8+
"1.3.6.1.4.1.34380.1.1.6": "pp_product",
9+
"1.3.6.1.4.1.34380.1.1.7": "pp_project",
10+
"1.3.6.1.4.1.34380.1.1.8": "pp_application",
11+
"1.3.6.1.4.1.34380.1.1.9": "pp_service",
12+
"1.3.6.1.4.1.34380.1.1.10": "pp_employee",
13+
"1.3.6.1.4.1.34380.1.1.11": "pp_created_by",
14+
"1.3.6.1.4.1.34380.1.1.12": "pp_environment",
15+
"1.3.6.1.4.1.34380.1.1.13": "pp_role",
16+
"1.3.6.1.4.1.34380.1.1.14": "pp_software_version",
17+
"1.3.6.1.4.1.34380.1.1.15": "pp_department",
18+
"1.3.6.1.4.1.34380.1.1.16": "pp_cluster",
19+
"1.3.6.1.4.1.34380.1.1.17": "pp_provisioner",
20+
"1.3.6.1.4.1.34380.1.1.18": "pp_region",
21+
"1.3.6.1.4.1.34380.1.1.19": "pp_datacenter",
22+
"1.3.6.1.4.1.34380.1.1.20": "pp_zone",
23+
"1.3.6.1.4.1.34380.1.1.21": "pp_network",
24+
"1.3.6.1.4.1.34380.1.1.22": "pp_securitypolicy",
25+
"1.3.6.1.4.1.34380.1.1.23": "pp_cloudplatform",
26+
"1.3.6.1.4.1.34380.1.1.24": "pp_apptier",
27+
"1.3.6.1.4.1.34380.1.1.25": "pp_hostname"
28+
};
29+
30+
export const HIERA_EDITOR_FIELD = "1.3.6.1.4.1.34380.1.1.7";
31+
export const HIERA_EDITOR_VALUE = "hiera_editor";

src/puppet/environment.ts

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import { PuppetHTTP } from "./http"
1717
import { isArray } from "util";
1818
import { WorkspaceSettings } from "./workspace_settings";
1919
import { EnvironmentTreeDump, NodeDump } from "../ipc/objects"
20+
import { HIERA_EDITOR_FIELD, HIERA_EDITOR_VALUE } from "./cert"
2021

22+
const forge = require("node-forge");
2123
const PromisePool = require('es6-promise-pool');
2224

2325
export class Environment
@@ -33,6 +35,7 @@ export class Environment
3335
private readonly _warnings: WorkspaceError[];
3436
private readonly _offline: boolean;
3537
private readonly _nodeFacts: Dictionary<string, any>;
38+
private readonly _certificates: Dictionary<string, any>;
3639
private _modulesInfo: PuppetModulesInfo;
3740

3841
constructor(workspace: Workspace, name: string, _path: string, cachePath: string, offline: boolean = false)
@@ -48,6 +51,7 @@ export class Environment
4851
this._global.put("environment", name);
4952
this._nodes = new Dictionary();
5053
this._nodeFacts = new Dictionary();
54+
this._certificates = new Dictionary();
5155
this._offline = offline;
5256
}
5357

@@ -96,6 +100,11 @@ export class Environment
96100
return path.join(this._cachePath, "facts", certName + ".json")
97101
}
98102

103+
public get certificateCachePath(): string
104+
{
105+
return path.join(this._cachePath, "certs")
106+
}
107+
99108
public get certListCachePath(): string
100109
{
101110
return path.join(this._cachePath, "certnames.json")
@@ -436,6 +445,69 @@ export class Environment
436445

437446
}
438447

448+
public getCertificate(certname: string): any
449+
{
450+
return this._certificates.get(certname);
451+
}
452+
453+
private async loadCertificates(updateProgressCategory: any, certList: string[], settings: WorkspaceSettings)
454+
{
455+
456+
if (!await async.isDirectory(this.certificateCachePath))
457+
{
458+
await async.makeDirectory(this.certificateCachePath);
459+
}
460+
461+
const certsExist = [];
462+
463+
for (const certname of certList)
464+
{
465+
certsExist.push(async.isFile(path.join(this.certificateCachePath, certname + ".txt")));
466+
}
467+
468+
const ex = await Promise.all(certsExist.map(p => p.catch(() => undefined)));
469+
const loadCerts = [];
470+
471+
for (let i = 0, t = certList.length; i < t; i++)
472+
{
473+
const certname = certList[i];
474+
const exists = ex[i];
475+
476+
if (exists)
477+
{
478+
loadCerts.push(async.readFile(path.join(this.certificateCachePath, certname + ".txt")));
479+
}
480+
else
481+
{
482+
loadCerts.push(Promise.resolve(null));
483+
}
484+
}
485+
486+
const certs = await Promise.all(loadCerts.map(p => p.catch((e: any): any => undefined)));
487+
488+
for (let i = 0, t = certList.length; i < t; i++)
489+
{
490+
const certname = certList[i];
491+
let certificate = certs[i];
492+
493+
if (certificate == null)
494+
{
495+
if (updateProgressCategory) updateProgressCategory(
496+
"[" + this.name + "] Downloading certificate for node " + certname + "...", false);
497+
498+
certificate = await PuppetHTTP.GetCertificate(certname, this.name, settings);
499+
await async.writeFile(path.join(this.certificateCachePath, certname + ".txt"), certificate);
500+
}
501+
502+
const cert = forge.pki.certificateFromPem(certificate);
503+
504+
if (cert == null)
505+
continue;
506+
507+
this._certificates.put(certname, cert);
508+
}
509+
}
510+
439511
private async updateCertList(updateProgressCategory: any, settings: WorkspaceSettings): Promise<string[]>
440512
{
441513
if (updateProgressCategory) updateProgressCategory("[" + this.name + "] Updating certificate list...", false);
@@ -451,12 +523,16 @@ export class Environment
451523
throw new WorkspaceError("Failed to obtain certificate list", e.toString());
452524
}
453525

526+
await this.loadCertificates(updateProgressCategory, certList, settings);
527+
454528
await async.writeJSON(this.certListCachePath, certList);
455529
return certList;
456530
}
457531

458532
public async init(progressCallback: any = null, updateProgressCategory: any = null): Promise<any>
459533
{
534+
const zis = this;
535+
460536
this._warnings.length = 0;
461537

462538
if (!await async.isDirectory(this.cachePath))
@@ -519,7 +595,26 @@ export class Environment
519595
}
520596

521597
const nodeIgnoreList = this.workspace.getNodeIgnoreList();
522-
nodeList = nodeList.filter((value: string) => nodeIgnoreList.indexOf(value) < 0);
598+
nodeList = nodeList.filter((certname: string) =>
599+
{
600+
if (nodeIgnoreList.indexOf(certname) >= 0)
601+
return false;
602+
603+
const cert = zis._certificates.get(certname);
604+
if (cert != null)
605+
{
606+
const ext = cert.getExtension({id: HIERA_EDITOR_FIELD});
607+
if (ext != null)
608+
{
609+
// cut the fist two characters
610+
// https://puppet.com/docs/puppet/6.0/ssl_attributes_extensions.html#manually-checking-for-extensions-in-csrs-and-certificates
611+
if (ext.value.substr(2) == HIERA_EDITOR_VALUE)
612+
return false;
613+
}
614+
}
615+
616+
return true;
617+
});
523618
this._nodeFacts.clear();
524619

525620
if (!this._offline)

src/puppet/files.ts

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export class Folder
5555
return await dir.createFile(localPath);
5656
}
5757

58-
const entryPath = path.join(this._path, File.FilePath(name));
58+
const entryPath = path.join(this._path, name);
5959
const node = await this.acquireFile(this._env, name, entryPath, slash(path.join(this._localPath, name)));
6060

6161
if (node == null)
@@ -120,7 +120,7 @@ export class Folder
120120
if (node == null)
121121
return false;
122122

123-
const entryPath = path.join(this._path, File.FilePath(name));
123+
const entryPath = path.join(this._path, name);
124124

125125
if (!await async.remove(entryPath))
126126
{
@@ -154,7 +154,7 @@ export class Folder
154154

155155
const nodes: any = [];
156156

157-
for (const node of await this.getNodes())
157+
for (const node of await this.getFiles())
158158
{
159159
nodes.push({
160160
"name": node.name,
@@ -197,7 +197,7 @@ export class Folder
197197

198198
public async getFile(name: string): Promise<File>
199199
{
200-
const entryPath = path.join(this._path, File.FilePath(name));
200+
const entryPath = path.join(this._path, name);
201201

202202
if (!await async.isFile(entryPath))
203203
{
@@ -247,7 +247,7 @@ export class Folder
247247
return result;
248248
}
249249

250-
public async getNodes(): Promise<Array<File>>
250+
public async getFiles(): Promise<Array<File>>
251251
{
252252
if (!await async.fileExists(this._path))
253253
{
@@ -261,19 +261,14 @@ export class Folder
261261

262262
const result:Array<File> = [];
263263

264-
for (const entry of await async.listFiles(this._path))
264+
for (const fileName of await async.listFiles(this._path))
265265
{
266-
const nodeName = File.ValidatePath(entry);
267-
268-
if (nodeName == null)
269-
continue;
270-
271-
const entryPath = path.join(this._path, entry);
266+
const entryPath = path.join(this._path, fileName);
272267

273268
if (await async.isFile(entryPath))
274269
{
275270
result.push(await this.acquireFile(
276-
this._env, nodeName, entryPath, slash(path.join(this._localPath, nodeName))));
271+
this._env, fileName, entryPath, slash(path.join(this._localPath, fileName))));
277272
}
278273
}
279274

@@ -315,19 +310,6 @@ export class File
315310
this._parent = parent;
316311
}
317312

318-
static FilePath(name: string): string
319-
{
320-
return name + ".yaml";
321-
}
322-
323-
static ValidatePath(pathName: string): string
324-
{
325-
if (!pathName.endsWith(".yaml"))
326-
return null;
327-
328-
return pathName.substr(0, pathName.length - 5);
329-
}
330-
331313
public async remove(): Promise<boolean>
332314
{
333315
if (this._parent == null)

src/puppet/hiera.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ export class Hierarchy
160160

161161
this._datadir = "data";
162162
this._hierarchy = [
163-
new HierarchyEntry("nodes/%{::trusted.certname}"),
164-
new HierarchyEntry("common")
163+
new HierarchyEntry("nodes/%{::trusted.certname}.yaml"),
164+
new HierarchyEntry("common.yaml")
165165
];
166166
}
167167

@@ -188,7 +188,7 @@ export class Hierarchy
188188
{
189189
if (value == null)
190190
return "";
191-
const key = path.pop();
191+
const key = path.shift();
192192
value = value[key];
193193
}
194194

@@ -212,7 +212,8 @@ export class Hierarchy
212212

213213
try
214214
{
215-
data = await async.readYAML(this._path);
215+
const document = await async.readYAML(this._path);
216+
data = document.toJSON();
216217
}
217218
catch (e)
218219
{

0 commit comments

Comments
 (0)