-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauthorizedKeys.ts
More file actions
83 lines (73 loc) · 2.6 KB
/
Copy pathauthorizedKeys.ts
File metadata and controls
83 lines (73 loc) · 2.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import fs from 'fs';
import type { Stats } from 'fs';
import { ParsedKey, utils } from 'ssh2';
export class AuthorizedKeysStore {
private keys: ParsedKey[] = [];
private reloadTimer: NodeJS.Timeout | null = null;
private watching = false;
constructor(
public readonly filePath: string,
private readonly watchInterval = 500,
) { }
get current(): readonly ParsedKey[] {
return this.keys;
}
reload(initial = false): number {
let content = '';
try {
content = fs.readFileSync(this.filePath, 'utf-8');
} catch (e: any) {
if (e?.code !== 'ENOENT') {
console.error(`[!] Failed to reload authorized keys from ${this.filePath}: ${e.message}`);
return this.keys.length;
}
}
const nextKeys: ParsedKey[] = [];
let invalidCount = 0;
for (const line of content.split(/\r?\n/)) {
const keyText = line.trim();
if (!keyText || keyText.startsWith('#')) continue;
const parsed = utils.parseKey(keyText);
if (parsed instanceof Error) {
invalidCount++;
continue;
}
nextKeys.push(parsed);
}
this.keys = nextKeys;
const action = initial ? 'Loaded' : 'Reloaded';
console.log(`[*] ${action} ${nextKeys.length} authorized key(s) from ${this.filePath}`);
if (invalidCount > 0) {
console.error(`[!] Ignored ${invalidCount} invalid authorized key line(s)`);
}
return nextKeys.length;
}
startWatching() {
if (this.watching) return;
this.watching = true;
this.reload(true);
fs.watchFile(this.filePath, { interval: this.watchInterval }, this.handleFileChange);
}
stopWatching() {
if (!this.watching) return;
this.watching = false;
if (this.reloadTimer) {
clearTimeout(this.reloadTimer);
this.reloadTimer = null;
}
fs.unwatchFile(this.filePath, this.handleFileChange);
}
private handleFileChange = (current: Stats, previous: Stats) => {
const changed = current.mtimeMs !== previous.mtimeMs
|| current.ctimeMs !== previous.ctimeMs
|| current.size !== previous.size
|| current.ino !== previous.ino
|| current.nlink !== previous.nlink;
if (!changed) return;
if (this.reloadTimer) clearTimeout(this.reloadTimer);
this.reloadTimer = setTimeout(() => {
this.reloadTimer = null;
this.reload();
}, 100);
};
}