Skip to content

Commit 7e31b34

Browse files
Merge pull request #55 from SmythOS/fix/core/json-vault-connector-list-keys-issue
added watcher in smyth json file connector to reflect file changes
2 parents c595eb0 + 4456b8e commit 7e31b34

File tree

2 files changed

+102
-42
lines changed

2 files changed

+102
-42
lines changed

packages/core/src/subsystems/Security/Vault.service/connectors/JSONFileVault.class.ts

Lines changed: 56 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -28,55 +28,17 @@ export class JSONFileVault extends VaultConnector {
2828
private vaultData: any;
2929
private index: any;
3030
private shared: string;
31+
private vaultFile: string;
3132

3233
constructor(protected _settings: JSONFileVaultConfig) {
3334
super(_settings);
3435
//if (!SmythRuntime.Instance) throw new Error('SRE not initialized');
3536

3637
this.shared = _settings.shared || ''; //if config.shared, all keys are accessible to all teams, and they are set under the 'shared' teamId
3738

38-
let vaultFile = this.findVaultFile(_settings.file);
39-
this.vaultData = {};
40-
if (fs.existsSync(vaultFile)) {
41-
try {
42-
if (_settings.fileKey && fs.existsSync(_settings.fileKey)) {
43-
try {
44-
const privateKey = fs.readFileSync(_settings.fileKey, 'utf8');
45-
const encryptedVault = fs.readFileSync(vaultFile, 'utf8').toString();
46-
const decryptedBuffer = crypto.privateDecrypt(
47-
{
48-
key: privateKey,
49-
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
50-
},
51-
Buffer.from(encryptedVault, 'base64')
52-
);
53-
this.vaultData = JSON.parse(decryptedBuffer.toString('utf8'));
54-
} catch (error) {
55-
throw new Error('Failed to decrypt vault');
56-
}
57-
} else {
58-
this.vaultData = JSON.parse(fs.readFileSync(vaultFile).toString());
59-
}
60-
} catch (e) {
61-
console.error('Error parsing vault file:', e);
62-
console.error('!!! Vault features might not work properly !!!');
63-
this.vaultData = {};
64-
}
65-
66-
if (this.vaultData?.encrypted && this.vaultData?.algorithm && this.vaultData?.data) {
67-
//this is an encrypted vault we need to request the master key
68-
this.setInteraction(this.getMasterKeyInteractive.bind(this));
69-
}
70-
71-
for (let teamId in this.vaultData) {
72-
for (let resourceId in this.vaultData[teamId]) {
73-
if (!this.index) this.index = {};
74-
if (!this.index[resourceId]) this.index[resourceId] = {};
75-
const value = this.vaultData[teamId][resourceId];
76-
this.index[resourceId][teamId] = value;
77-
}
78-
}
79-
}
39+
this.vaultFile = this.findVaultFile(_settings.file);
40+
this.fetchVaultData(this.vaultFile, _settings);
41+
this.initFileWatcher();
8042
}
8143

8244
private findVaultFile(vaultFile) {
@@ -192,4 +154,56 @@ export class JSONFileVault extends VaultConnector {
192154

193155
return acl;
194156
}
157+
158+
private fetchVaultData(vaultFile: string, _settings: JSONFileVaultConfig) {
159+
160+
if (fs.existsSync(vaultFile)) {
161+
try {
162+
if (_settings.fileKey && fs.existsSync(_settings.fileKey)) {
163+
try {
164+
const privateKey = fs.readFileSync(_settings.fileKey, 'utf8');
165+
const encryptedVault = fs.readFileSync(vaultFile, 'utf8').toString();
166+
const decryptedBuffer = crypto.privateDecrypt(
167+
{
168+
key: privateKey,
169+
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
170+
},
171+
Buffer.from(encryptedVault, 'base64')
172+
);
173+
this.vaultData = JSON.parse(decryptedBuffer.toString('utf8'));
174+
} catch (error) {
175+
throw new Error('Failed to decrypt vault');
176+
}
177+
} else {
178+
this.vaultData = JSON.parse(fs.readFileSync(vaultFile).toString());
179+
}
180+
} catch (e) {
181+
console.error('Error parsing vault file:', e);
182+
console.error('!!! Vault features might not work properly !!!');
183+
this.vaultData = {};
184+
}
185+
186+
if (this.vaultData?.encrypted && this.vaultData?.algorithm && this.vaultData?.data) {
187+
//this is an encrypted vault we need to request the master key
188+
this.setInteraction(this.getMasterKeyInteractive.bind(this));
189+
}
190+
191+
for (let teamId in this.vaultData) {
192+
for (let resourceId in this.vaultData[teamId]) {
193+
if (!this.index) this.index = {};
194+
if (!this.index[resourceId]) this.index[resourceId] = {};
195+
const value = this.vaultData[teamId][resourceId];
196+
this.index[resourceId][teamId] = value;
197+
}
198+
}
199+
}
200+
}
201+
202+
private initFileWatcher() {
203+
fs.watch(this.vaultFile, (eventType, filename) => {
204+
if (eventType === 'change') {
205+
this.fetchVaultData(this.vaultFile, this._settings);
206+
}
207+
});
208+
}
195209
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { setupSRE } from '../../utils/sre';
3+
import { ConnectorService } from '@sre/Core/ConnectorsService';
4+
import { IAccessCandidate, TAccessRole } from 'index';
5+
6+
setupSRE({
7+
Vault: {
8+
Connector: 'JSONFileVault',
9+
Settings: {
10+
file: '/Users/zubair/Zubair/SmythOS/smyth-opensource/smythos-ui/vault.json',
11+
},
12+
},
13+
Log: {
14+
Connector: 'ConsoleLog',
15+
},
16+
});
17+
18+
describe('JSONFileVault Tests', () => {
19+
it(
20+
'List all keys in the vault',
21+
async () => {
22+
const mockCandidate: IAccessCandidate = {
23+
id: 'default',
24+
role: TAccessRole.Team,
25+
};
26+
27+
const vaultConnector = ConnectorService.getVaultConnector('JSONFileVault');
28+
const result = await vaultConnector.team(mockCandidate.id).listKeys();
29+
expect(result).toBeDefined();
30+
},
31+
);
32+
33+
it(
34+
'Get a key from the vault',
35+
async () => {
36+
const mockCandidate: IAccessCandidate = {
37+
id: 'default',
38+
role: TAccessRole.Team,
39+
};
40+
41+
const vaultConnector = ConnectorService.getVaultConnector('JSONFileVault');
42+
const result = await vaultConnector.team(mockCandidate.id).get('testKey');
43+
expect(result).toBe('testValue');
44+
},
45+
);
46+
});

0 commit comments

Comments
 (0)