diff --git a/packages/core/src/subsystems/Security/Vault.service/connectors/JSONFileVault.class.ts b/packages/core/src/subsystems/Security/Vault.service/connectors/JSONFileVault.class.ts index fe35e1eaf..a4409fb83 100644 --- a/packages/core/src/subsystems/Security/Vault.service/connectors/JSONFileVault.class.ts +++ b/packages/core/src/subsystems/Security/Vault.service/connectors/JSONFileVault.class.ts @@ -28,6 +28,7 @@ export class JSONFileVault extends VaultConnector { private vaultData: any; private index: any; private shared: string; + private vaultFile: string; constructor(protected _settings: JSONFileVaultConfig) { super(_settings); @@ -35,48 +36,9 @@ export class JSONFileVault extends VaultConnector { this.shared = _settings.shared || ''; //if config.shared, all keys are accessible to all teams, and they are set under the 'shared' teamId - let vaultFile = this.findVaultFile(_settings.file); - this.vaultData = {}; - if (fs.existsSync(vaultFile)) { - try { - if (_settings.fileKey && fs.existsSync(_settings.fileKey)) { - try { - const privateKey = fs.readFileSync(_settings.fileKey, 'utf8'); - const encryptedVault = fs.readFileSync(vaultFile, 'utf8').toString(); - const decryptedBuffer = crypto.privateDecrypt( - { - key: privateKey, - padding: crypto.constants.RSA_PKCS1_OAEP_PADDING, - }, - Buffer.from(encryptedVault, 'base64') - ); - this.vaultData = JSON.parse(decryptedBuffer.toString('utf8')); - } catch (error) { - throw new Error('Failed to decrypt vault'); - } - } else { - this.vaultData = JSON.parse(fs.readFileSync(vaultFile).toString()); - } - } catch (e) { - console.error('Error parsing vault file:', e); - console.error('!!! Vault features might not work properly !!!'); - this.vaultData = {}; - } - - if (this.vaultData?.encrypted && this.vaultData?.algorithm && this.vaultData?.data) { - //this is an encrypted vault we need to request the master key - this.setInteraction(this.getMasterKeyInteractive.bind(this)); - } - - for (let teamId in this.vaultData) { - for (let resourceId in this.vaultData[teamId]) { - if (!this.index) this.index = {}; - if (!this.index[resourceId]) this.index[resourceId] = {}; - const value = this.vaultData[teamId][resourceId]; - this.index[resourceId][teamId] = value; - } - } - } + this.vaultFile = this.findVaultFile(_settings.file); + this.fetchVaultData(this.vaultFile, _settings); + this.initFileWatcher(); } private findVaultFile(vaultFile) { @@ -192,4 +154,56 @@ export class JSONFileVault extends VaultConnector { return acl; } + + private fetchVaultData(vaultFile: string, _settings: JSONFileVaultConfig) { + + if (fs.existsSync(vaultFile)) { + try { + if (_settings.fileKey && fs.existsSync(_settings.fileKey)) { + try { + const privateKey = fs.readFileSync(_settings.fileKey, 'utf8'); + const encryptedVault = fs.readFileSync(vaultFile, 'utf8').toString(); + const decryptedBuffer = crypto.privateDecrypt( + { + key: privateKey, + padding: crypto.constants.RSA_PKCS1_OAEP_PADDING, + }, + Buffer.from(encryptedVault, 'base64') + ); + this.vaultData = JSON.parse(decryptedBuffer.toString('utf8')); + } catch (error) { + throw new Error('Failed to decrypt vault'); + } + } else { + this.vaultData = JSON.parse(fs.readFileSync(vaultFile).toString()); + } + } catch (e) { + console.error('Error parsing vault file:', e); + console.error('!!! Vault features might not work properly !!!'); + this.vaultData = {}; + } + + if (this.vaultData?.encrypted && this.vaultData?.algorithm && this.vaultData?.data) { + //this is an encrypted vault we need to request the master key + this.setInteraction(this.getMasterKeyInteractive.bind(this)); + } + + for (let teamId in this.vaultData) { + for (let resourceId in this.vaultData[teamId]) { + if (!this.index) this.index = {}; + if (!this.index[resourceId]) this.index[resourceId] = {}; + const value = this.vaultData[teamId][resourceId]; + this.index[resourceId][teamId] = value; + } + } + } + } + + private initFileWatcher() { + fs.watch(this.vaultFile, (eventType, filename) => { + if (eventType === 'change') { + this.fetchVaultData(this.vaultFile, this._settings); + } + }); + } } diff --git a/packages/core/tests/unit/core/JSONFileVaultConnector.test.ts b/packages/core/tests/unit/core/JSONFileVaultConnector.test.ts new file mode 100644 index 000000000..f56b7d455 --- /dev/null +++ b/packages/core/tests/unit/core/JSONFileVaultConnector.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from 'vitest'; +import { setupSRE } from '../../utils/sre'; +import { ConnectorService } from '@sre/Core/ConnectorsService'; +import { IAccessCandidate, TAccessRole } from 'index'; + +setupSRE({ + Vault: { + Connector: 'JSONFileVault', + Settings: { + file: '/Users/zubair/Zubair/SmythOS/smyth-opensource/smythos-ui/vault.json', + }, + }, + Log: { + Connector: 'ConsoleLog', + }, +}); + +describe('JSONFileVault Tests', () => { + it( + 'List all keys in the vault', + async () => { + const mockCandidate: IAccessCandidate = { + id: 'default', + role: TAccessRole.Team, + }; + + const vaultConnector = ConnectorService.getVaultConnector('JSONFileVault'); + const result = await vaultConnector.team(mockCandidate.id).listKeys(); + expect(result).toBeDefined(); + }, + ); + + it( + 'Get a key from the vault', + async () => { + const mockCandidate: IAccessCandidate = { + id: 'default', + role: TAccessRole.Team, + }; + + const vaultConnector = ConnectorService.getVaultConnector('JSONFileVault'); + const result = await vaultConnector.team(mockCandidate.id).get('testKey'); + expect(result).toBe('testValue'); + }, + ); +}); \ No newline at end of file