diff --git a/tests/secrets/create.test.ts b/tests/secrets/create.test.ts new file mode 100644 index 00000000..ac880e79 --- /dev/null +++ b/tests/secrets/create.test.ts @@ -0,0 +1,88 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import { vaultOps } from 'polykey/dist/vaults'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandCreateSecret', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let polykeyAgent: PolykeyAgent; + let passwordFile: string; + let command: Array; + + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test( + 'should create secrets', + async () => { + const vaultName = 'Vault1' as VaultName; + const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); + const secretPath = path.join(dataDir, 'secret'); + await fs.promises.writeFile(secretPath, 'this is a secret'); + + command = [ + 'secrets', + 'create', + '-np', + dataDir, + secretPath, + `${vaultName}:MySecret`, + ]; + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + const list = await vaultOps.listSecrets(vault); + expect(list.sort()).toStrictEqual(['MySecret']); + expect( + (await vaultOps.getSecret(vault, 'MySecret')).toString(), + ).toStrictEqual('this is a secret'); + }); + }, + globalThis.defaultTimeout * 2, + ); +}); diff --git a/tests/secrets/delete.test.ts b/tests/secrets/delete.test.ts new file mode 100644 index 00000000..9a345167 --- /dev/null +++ b/tests/secrets/delete.test.ts @@ -0,0 +1,78 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import { vaultOps } from 'polykey/dist/vaults'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandDeleteSecret', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let polykeyAgent: PolykeyAgent; + let passwordFile: string; + let command: Array; + + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('should delete secrets', async () => { + const vaultName = 'Vault2' as VaultName; + const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + await vaultOps.addSecret(vault, 'MySecret', 'this is the secret'); + const list = await vaultOps.listSecrets(vault); + expect(list.sort()).toStrictEqual(['MySecret']); + }); + + command = ['secrets', 'delete', '-np', dataDir, `${vaultName}:MySecret`]; + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + const list = await vaultOps.listSecrets(vault); + expect(list.sort()).toStrictEqual([]); + }); + }); +}); diff --git a/tests/secrets/get.test.ts b/tests/secrets/get.test.ts new file mode 100644 index 00000000..d2c3d0fb --- /dev/null +++ b/tests/secrets/get.test.ts @@ -0,0 +1,72 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import { vaultOps } from 'polykey/dist/vaults'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandGetSecret', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let polykeyAgent: PolykeyAgent; + let passwordFile: string; + let command: Array; + + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('should retrieve secrets', async () => { + const vaultName = 'Vault3' as VaultName; + const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + await vaultOps.addSecret(vault, 'MySecret', 'this is the secret'); + }); + + command = ['secrets', 'get', '-np', dataDir, `${vaultName}:MySecret`]; + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.stdout).toBe('this is the secret'); + expect(result.exitCode).toBe(0); + }); +}); diff --git a/tests/secrets/list.test.ts b/tests/secrets/list.test.ts new file mode 100644 index 00000000..f1b88b71 --- /dev/null +++ b/tests/secrets/list.test.ts @@ -0,0 +1,77 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import { vaultOps } from 'polykey/dist/vaults'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandListSecrets', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let polykeyAgent: PolykeyAgent; + let passwordFile: string; + let command: Array; + + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test( + 'should list secrets', + async () => { + const vaultName = 'Vault4' as VaultName; + const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + await vaultOps.addSecret(vault, 'MySecret1', 'this is the secret 1'); + await vaultOps.addSecret(vault, 'MySecret2', 'this is the secret 2'); + await vaultOps.addSecret(vault, 'MySecret3', 'this is the secret 3'); + }); + + command = ['secrets', 'list', '-np', dataDir, vaultName]; + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + }, + globalThis.defaultTimeout * 2, + ); +}); diff --git a/tests/secrets/newDir.test.ts b/tests/secrets/newDir.test.ts new file mode 100644 index 00000000..74797011 --- /dev/null +++ b/tests/secrets/newDir.test.ts @@ -0,0 +1,88 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import { vaultOps } from 'polykey/dist/vaults'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandNewDir', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let polykeyAgent: PolykeyAgent; + let passwordFile: string; + let command: Array; + + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('should make a directory', async () => { + const vaultName = 'Vault5' as VaultName; + const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); + + command = [ + 'secrets', + 'mkdir', + '-np', + dataDir, + `${vaultName}:dir1/dir2`, + '-r', + ]; + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + await vaultOps.addSecret(vault, 'dir1/MySecret1', 'this is the secret 1'); + await vaultOps.addSecret( + vault, + 'dir1/dir2/MySecret2', + 'this is the secret 2', + ); + + const list = await vaultOps.listSecrets(vault); + expect(list.sort()).toStrictEqual( + ['dir1/MySecret1', 'dir1/dir2/MySecret2'].sort(), + ); + }); + }); +}); diff --git a/tests/secrets/newDirSecret.test.ts b/tests/secrets/newDirSecret.test.ts new file mode 100644 index 00000000..9bdb9719 --- /dev/null +++ b/tests/secrets/newDirSecret.test.ts @@ -0,0 +1,96 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import { vaultOps } from 'polykey/dist/vaults'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandNewDirSecret', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let polykeyAgent: PolykeyAgent; + let passwordFile: string; + let command: Array; + + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('should add a directory of secrets', async () => { + const vaultName = 'Vault8' as VaultName; + const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); + + const secretDir = path.join(dataDir, 'secrets'); + await fs.promises.mkdir(secretDir); + await fs.promises.writeFile( + path.join(secretDir, 'secret-1'), + 'this is the secret 1', + ); + await fs.promises.writeFile( + path.join(secretDir, 'secret-2'), + 'this is the secret 2', + ); + await fs.promises.writeFile( + path.join(secretDir, 'secret-3'), + 'this is the secret 3', + ); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + const list = await vaultOps.listSecrets(vault); + expect(list.sort()).toStrictEqual([]); + }); + + command = ['secrets', 'dir', '-np', dataDir, secretDir, vaultName]; + + const result2 = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result2.exitCode).toBe(0); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + const list = await vaultOps.listSecrets(vault); + expect(list.sort()).toStrictEqual([ + 'secrets/secret-1', + 'secrets/secret-2', + 'secrets/secret-3', + ]); + }); + }); +}); diff --git a/tests/secrets/rename.test.ts b/tests/secrets/rename.test.ts new file mode 100644 index 00000000..0e2aa903 --- /dev/null +++ b/tests/secrets/rename.test.ts @@ -0,0 +1,83 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import { vaultOps } from 'polykey/dist/vaults'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandRenameSecret', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let polykeyAgent: PolykeyAgent; + let passwordFile: string; + let command: Array; + + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('should rename secrets', async () => { + const vaultName = 'Vault6' as VaultName; + const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + await vaultOps.addSecret(vault, 'MySecret', 'this is the secret'); + }); + + command = [ + 'secrets', + 'rename', + '-np', + dataDir, + `${vaultName}:MySecret`, + 'MyRenamedSecret', + ]; + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + const list = await vaultOps.listSecrets(vault); + expect(list.sort()).toStrictEqual(['MyRenamedSecret']); + }); + }); +}); diff --git a/tests/secrets/secrets.test.ts b/tests/secrets/secrets.test.ts deleted file mode 100644 index 829fc939..00000000 --- a/tests/secrets/secrets.test.ts +++ /dev/null @@ -1,331 +0,0 @@ -import type { VaultName } from 'polykey/dist/vaults/types'; -import path from 'path'; -import fs from 'fs'; -import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; -import PolykeyAgent from 'polykey/dist/PolykeyAgent'; -import { vaultOps } from 'polykey/dist/vaults'; -import * as keysUtils from 'polykey/dist/keys/utils'; -import * as testUtils from '../utils'; - -describe('CLI secrets', () => { - const password = 'password'; - const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); - let dataDir: string; - let polykeyAgent: PolykeyAgent; - let passwordFile: string; - let command: Array; - - beforeEach(async () => { - dataDir = await fs.promises.mkdtemp( - path.join(globalThis.tmpDir, 'polykey-test-'), - ); - passwordFile = path.join(dataDir, 'passwordFile'); - await fs.promises.writeFile(passwordFile, 'password'); - polykeyAgent = await PolykeyAgent.createPolykeyAgent({ - password, - options: { - nodePath: dataDir, - agentServiceHost: '127.0.0.1', - clientServiceHost: '127.0.0.1', - keys: { - passwordOpsLimit: keysUtils.passwordOpsLimits.min, - passwordMemLimit: keysUtils.passwordMemLimits.min, - strictMemoryLock: false, - }, - }, - logger: logger, - }); - // Authorize session - await testUtils.pkStdio( - ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], - { - env: {}, - cwd: dataDir, - }, - ); - }); - afterEach(async () => { - await polykeyAgent.stop(); - await fs.promises.rm(dataDir, { - force: true, - recursive: true, - }); - }); - - describe('commandCreateSecret', () => { - test( - 'should create secrets', - async () => { - const vaultName = 'Vault1' as VaultName; - const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); - const secretPath = path.join(dataDir, 'secret'); - await fs.promises.writeFile(secretPath, 'this is a secret'); - - command = [ - 'secrets', - 'create', - '-np', - dataDir, - secretPath, - `${vaultName}:MySecret`, - ]; - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(0); - - await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - const list = await vaultOps.listSecrets(vault); - expect(list.sort()).toStrictEqual(['MySecret']); - expect( - (await vaultOps.getSecret(vault, 'MySecret')).toString(), - ).toStrictEqual('this is a secret'); - }); - }, - globalThis.defaultTimeout * 2, - ); - }); - describe('commandDeleteSecret', () => { - test('should delete secrets', async () => { - const vaultName = 'Vault2' as VaultName; - const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); - - await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - await vaultOps.addSecret(vault, 'MySecret', 'this is the secret'); - const list = await vaultOps.listSecrets(vault); - expect(list.sort()).toStrictEqual(['MySecret']); - }); - - command = ['secrets', 'delete', '-np', dataDir, `${vaultName}:MySecret`]; - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(0); - - await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - const list = await vaultOps.listSecrets(vault); - expect(list.sort()).toStrictEqual([]); - }); - }); - }); - describe('commandGetSecret', () => { - test('should retrieve secrets', async () => { - const vaultName = 'Vault3' as VaultName; - const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); - - await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - await vaultOps.addSecret(vault, 'MySecret', 'this is the secret'); - }); - - command = ['secrets', 'get', '-np', dataDir, `${vaultName}:MySecret`]; - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.stdout).toBe('this is the secret'); - expect(result.exitCode).toBe(0); - }); - }); - describe('commandListSecrets', () => { - test( - 'should list secrets', - async () => { - const vaultName = 'Vault4' as VaultName; - const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); - - await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - await vaultOps.addSecret(vault, 'MySecret1', 'this is the secret 1'); - await vaultOps.addSecret(vault, 'MySecret2', 'this is the secret 2'); - await vaultOps.addSecret(vault, 'MySecret3', 'this is the secret 3'); - }); - - command = ['secrets', 'list', '-np', dataDir, vaultName]; - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(0); - }, - globalThis.defaultTimeout * 2, - ); - }); - describe('commandNewDir', () => { - test('should make a directory', async () => { - const vaultName = 'Vault5' as VaultName; - const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); - - command = [ - 'secrets', - 'mkdir', - '-np', - dataDir, - `${vaultName}:dir1/dir2`, - '-r', - ]; - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(0); - - await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - await vaultOps.addSecret( - vault, - 'dir1/MySecret1', - 'this is the secret 1', - ); - await vaultOps.addSecret( - vault, - 'dir1/dir2/MySecret2', - 'this is the secret 2', - ); - - const list = await vaultOps.listSecrets(vault); - expect(list.sort()).toStrictEqual( - ['dir1/MySecret1', 'dir1/dir2/MySecret2'].sort(), - ); - }); - }); - }); - describe('commandRenameSecret', () => { - test('should rename secrets', async () => { - const vaultName = 'Vault6' as VaultName; - const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); - - await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - await vaultOps.addSecret(vault, 'MySecret', 'this is the secret'); - }); - - command = [ - 'secrets', - 'rename', - '-np', - dataDir, - `${vaultName}:MySecret`, - 'MyRenamedSecret', - ]; - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(0); - - await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - const list = await vaultOps.listSecrets(vault); - expect(list.sort()).toStrictEqual(['MyRenamedSecret']); - }); - }); - }); - describe('commandUpdateSecret', () => { - test('should update secrets', async () => { - const vaultName = 'Vault7' as VaultName; - const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); - - const secretPath = path.join(dataDir, 'secret'); - await fs.promises.writeFile(secretPath, 'updated-content'); - - await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - await vaultOps.addSecret(vault, 'MySecret', 'original-content'); - expect( - (await vaultOps.getSecret(vault, 'MySecret')).toString(), - ).toStrictEqual('original-content'); - }); - - command = [ - 'secrets', - 'update', - '-np', - dataDir, - secretPath, - `${vaultName}:MySecret`, - ]; - - const result2 = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result2.exitCode).toBe(0); - - await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - const list = await vaultOps.listSecrets(vault); - expect(list.sort()).toStrictEqual(['MySecret']); - expect( - (await vaultOps.getSecret(vault, 'MySecret')).toString(), - ).toStrictEqual('updated-content'); - }); - }); - }); - describe('commandNewDirSecret', () => { - test('should add a directory of secrets', async () => { - const vaultName = 'Vault8' as VaultName; - const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); - - const secretDir = path.join(dataDir, 'secrets'); - await fs.promises.mkdir(secretDir); - await fs.promises.writeFile( - path.join(secretDir, 'secret-1'), - 'this is the secret 1', - ); - await fs.promises.writeFile( - path.join(secretDir, 'secret-2'), - 'this is the secret 2', - ); - await fs.promises.writeFile( - path.join(secretDir, 'secret-3'), - 'this is the secret 3', - ); - - await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - const list = await vaultOps.listSecrets(vault); - expect(list.sort()).toStrictEqual([]); - }); - - command = ['secrets', 'dir', '-np', dataDir, secretDir, vaultName]; - - const result2 = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result2.exitCode).toBe(0); - - await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - const list = await vaultOps.listSecrets(vault); - expect(list.sort()).toStrictEqual([ - 'secrets/secret-1', - 'secrets/secret-2', - 'secrets/secret-3', - ]); - }); - }); - }); - describe('commandStat', () => { - test('should retrieve secrets', async () => { - const vaultName = 'Vault9'; - const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); - - await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - await vaultOps.addSecret(vault, 'MySecret', 'this is the secret'); - }); - - command = ['secrets', 'stat', '-np', dataDir, `${vaultName}:MySecret`]; - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(0); - expect(result.stdout).toContain('nlink: 1'); - expect(result.stdout).toContain('blocks: 1'); - expect(result.stdout).toContain('blksize: 4096'); - expect(result.stdout).toContain('size: 18'); - }); - }); -}); diff --git a/tests/secrets/stat.test.ts b/tests/secrets/stat.test.ts new file mode 100644 index 00000000..95cbe51c --- /dev/null +++ b/tests/secrets/stat.test.ts @@ -0,0 +1,74 @@ +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import { vaultOps } from 'polykey/dist/vaults'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandStat', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let polykeyAgent: PolykeyAgent; + let passwordFile: string; + let command: Array; + + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('should retrieve secrets', async () => { + const vaultName = 'Vault9'; + const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + await vaultOps.addSecret(vault, 'MySecret', 'this is the secret'); + }); + + command = ['secrets', 'stat', '-np', dataDir, `${vaultName}:MySecret`]; + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + expect(result.stdout).toContain('nlink: 1'); + expect(result.stdout).toContain('blocks: 1'); + expect(result.stdout).toContain('blksize: 4096'); + expect(result.stdout).toContain('size: 18'); + }); +}); diff --git a/tests/secrets/uopdate.test.ts b/tests/secrets/uopdate.test.ts new file mode 100644 index 00000000..9cb9969e --- /dev/null +++ b/tests/secrets/uopdate.test.ts @@ -0,0 +1,92 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import { vaultOps } from 'polykey/dist/vaults'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandUpdateSecret', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let polykeyAgent: PolykeyAgent; + let passwordFile: string; + let command: Array; + + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('should update secrets', async () => { + const vaultName = 'Vault7' as VaultName; + const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); + + const secretPath = path.join(dataDir, 'secret'); + await fs.promises.writeFile(secretPath, 'updated-content'); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + await vaultOps.addSecret(vault, 'MySecret', 'original-content'); + expect( + (await vaultOps.getSecret(vault, 'MySecret')).toString(), + ).toStrictEqual('original-content'); + }); + + command = [ + 'secrets', + 'update', + '-np', + dataDir, + secretPath, + `${vaultName}:MySecret`, + ]; + + const result2 = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result2.exitCode).toBe(0); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + const list = await vaultOps.listSecrets(vault); + expect(list.sort()).toStrictEqual(['MySecret']); + expect( + (await vaultOps.getSecret(vault, 'MySecret')).toString(), + ).toStrictEqual('updated-content'); + }); + }); +}); diff --git a/tests/vaults/create.test.ts b/tests/vaults/create.test.ts new file mode 100644 index 00000000..2c942a6b --- /dev/null +++ b/tests/vaults/create.test.ts @@ -0,0 +1,96 @@ +import type { GestaltNodeInfo } from 'polykey/dist/gestalts/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import * as ids from 'polykey/dist/ids'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandCreateVaults', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let passwordFile: string; + let polykeyAgent: PolykeyAgent; + let command: Array; + const nodeIdGenerator = ids.createNodeIdGenerator(); + const nodeId1 = nodeIdGenerator(); + const nodeId2 = nodeIdGenerator(); + const nodeId3 = nodeIdGenerator(); + const node1: GestaltNodeInfo = { + nodeId: nodeId1, + }; + const node2: GestaltNodeInfo = { + nodeId: nodeId2, + }; + const node3: GestaltNodeInfo = { + nodeId: nodeId3, + }; + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + await polykeyAgent.gestaltGraph.setNode(node1); + await polykeyAgent.gestaltGraph.setNode(node2); + await polykeyAgent.gestaltGraph.setNode(node3); + + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + command = []; + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('should create vaults', async () => { + command = ['vaults', 'create', '-np', dataDir, 'MyTestVault']; + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + const result2 = await testUtils.pkStdio( + ['vaults', 'touch', '-np', dataDir, 'MyTestVault2'], + { + env: {}, + cwd: dataDir, + }, + ); + expect(result2.exitCode).toBe(0); + + const list = (await polykeyAgent.vaultManager.listVaults()).keys(); + const namesList: string[] = []; + for await (const name of list) { + namesList.push(name); + } + expect(namesList).toContain('MyTestVault'); + expect(namesList).toContain('MyTestVault2'); + }); +}); diff --git a/tests/vaults/delete.test.ts b/tests/vaults/delete.test.ts new file mode 100644 index 00000000..30bbee6a --- /dev/null +++ b/tests/vaults/delete.test.ts @@ -0,0 +1,105 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import type { GestaltNodeInfo } from 'polykey/dist/gestalts/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import * as ids from 'polykey/dist/ids'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandDeleteVault', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let passwordFile: string; + let polykeyAgent: PolykeyAgent; + let command: Array; + let vaultNumber: number; + let vaultName: VaultName; + const nodeIdGenerator = ids.createNodeIdGenerator(); + const nodeId1 = nodeIdGenerator(); + const nodeId2 = nodeIdGenerator(); + const nodeId3 = nodeIdGenerator(); + const node1: GestaltNodeInfo = { + nodeId: nodeId1, + }; + const node2: GestaltNodeInfo = { + nodeId: nodeId2, + }; + const node3: GestaltNodeInfo = { + nodeId: nodeId3, + }; + // Helper functions + function genVaultName() { + vaultNumber++; + return `vault-${vaultNumber}` as VaultName; + } + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + await polykeyAgent.gestaltGraph.setNode(node1); + await polykeyAgent.gestaltGraph.setNode(node2); + await polykeyAgent.gestaltGraph.setNode(node3); + + vaultNumber = 0; + + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + vaultName = genVaultName(); + command = []; + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('should delete vault', async () => { + command = ['vaults', 'delete', '-np', dataDir, vaultName]; + await polykeyAgent.vaultManager.createVault(vaultName); + let id = polykeyAgent.vaultManager.getVaultId(vaultName); + expect(id).toBeTruthy(); + + id = polykeyAgent.vaultManager.getVaultId(vaultName); + expect(id).toBeTruthy(); + + const result2 = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result2.exitCode).toBe(0); + + const list = (await polykeyAgent.vaultManager.listVaults()).keys(); + const namesList: string[] = []; + for await (const name of list) { + namesList.push(name); + } + expect(namesList).not.toContain(vaultName); + }); +}); diff --git a/tests/vaults/list.test.ts b/tests/vaults/list.test.ts new file mode 100644 index 00000000..131e02ec --- /dev/null +++ b/tests/vaults/list.test.ts @@ -0,0 +1,84 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import type { GestaltNodeInfo } from 'polykey/dist/gestalts/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import * as ids from 'polykey/dist/ids'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandListVaults', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let passwordFile: string; + let polykeyAgent: PolykeyAgent; + let command: Array; + const nodeIdGenerator = ids.createNodeIdGenerator(); + const nodeId1 = nodeIdGenerator(); + const nodeId2 = nodeIdGenerator(); + const nodeId3 = nodeIdGenerator(); + const node1: GestaltNodeInfo = { + nodeId: nodeId1, + }; + const node2: GestaltNodeInfo = { + nodeId: nodeId2, + }; + const node3: GestaltNodeInfo = { + nodeId: nodeId3, + }; + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + await polykeyAgent.gestaltGraph.setNode(node1); + await polykeyAgent.gestaltGraph.setNode(node2); + await polykeyAgent.gestaltGraph.setNode(node3); + + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + command = []; + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('should list all vaults', async () => { + command = ['vaults', 'list', '-np', dataDir]; + await polykeyAgent.vaultManager.createVault('Vault1' as VaultName); + await polykeyAgent.vaultManager.createVault('Vault2' as VaultName); + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + }); +}); diff --git a/tests/vaults/log.test.ts b/tests/vaults/log.test.ts new file mode 100644 index 00000000..513faa4f --- /dev/null +++ b/tests/vaults/log.test.ts @@ -0,0 +1,158 @@ +import type { VaultId, VaultName } from 'polykey/dist/vaults/types'; +import type { GestaltNodeInfo } from 'polykey/dist/gestalts/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import * as ids from 'polykey/dist/ids'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandVaultLog', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + const secret1 = { name: 'secret1', content: 'Secret-1-content' }; + const secret2 = { name: 'secret2', content: 'Secret-2-content' }; + + let dataDir: string; + let passwordFile: string; + let polykeyAgent: PolykeyAgent; + let vaultNumber: number; + let vaultName: VaultName; + let vaultId: VaultId; + let writeF1Oid: string; + let writeF2Oid: string; + let writeF3Oid: string; + + const nodeIdGenerator = ids.createNodeIdGenerator(); + const nodeId1 = nodeIdGenerator(); + const nodeId2 = nodeIdGenerator(); + const nodeId3 = nodeIdGenerator(); + const node1: GestaltNodeInfo = { + nodeId: nodeId1, + }; + const node2: GestaltNodeInfo = { + nodeId: nodeId2, + }; + const node3: GestaltNodeInfo = { + nodeId: nodeId3, + }; + // Helper functions + function genVaultName() { + vaultNumber++; + return `vault-${vaultNumber}` as VaultName; + } + + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + await polykeyAgent.gestaltGraph.setNode(node1); + await polykeyAgent.gestaltGraph.setNode(node2); + await polykeyAgent.gestaltGraph.setNode(node3); + + vaultNumber = 0; + + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + vaultName = genVaultName(); + + vaultId = await polykeyAgent.vaultManager.createVault(vaultName); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + await vault.writeF(async (efs) => { + await efs.writeFile(secret1.name, secret1.content); + }); + writeF1Oid = (await vault.log(undefined, 0))[0].commitId; + + await vault.writeF(async (efs) => { + await efs.writeFile(secret2.name, secret2.content); + }); + writeF2Oid = (await vault.log(undefined, 0))[0].commitId; + + await vault.writeF(async (efs) => { + await efs.unlink(secret2.name); + }); + writeF3Oid = (await vault.log(undefined, 0))[0].commitId; + }); + }); + afterEach(async () => { + await polykeyAgent.vaultManager.destroyVault(vaultId); + + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('Should get all writeFs', async () => { + const command = ['vaults', 'log', '-np', dataDir, vaultName]; + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toEqual(0); + expect(result.stdout).toContain(writeF1Oid); + expect(result.stdout).toContain(writeF2Oid); + expect(result.stdout).toContain(writeF3Oid); + }); + test('should get a part of the log', async () => { + const command = ['vaults', 'log', '-np', dataDir, '-d', '2', vaultName]; + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toEqual(0); + expect(result.stdout).not.toContain(writeF1Oid); + expect(result.stdout).toContain(writeF2Oid); + expect(result.stdout).toContain(writeF3Oid); + }); + test('should get a specific writeF', async () => { + const command = [ + 'vaults', + 'log', + '-np', + dataDir, + '-d', + '1', + vaultName, + '-ci', + writeF2Oid, + ]; + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toEqual(0); + expect(result.stdout).not.toContain(writeF1Oid); + expect(result.stdout).toContain(writeF2Oid); + expect(result.stdout).not.toContain(writeF3Oid); + }); + test.todo('test formatting of the output'); +}); diff --git a/tests/vaults/permissions.test.ts b/tests/vaults/permissions.test.ts new file mode 100644 index 00000000..121ed369 --- /dev/null +++ b/tests/vaults/permissions.test.ts @@ -0,0 +1,127 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import type { GestaltNodeInfo } from 'polykey/dist/gestalts/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import * as ids from 'polykey/dist/ids'; +import * as nodesUtils from 'polykey/dist/nodes/utils'; +import * as vaultsUtils from 'polykey/dist/vaults/utils'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandPermissions', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let passwordFile: string; + let polykeyAgent: PolykeyAgent; + let command: Array; + let vaultNumber: number; + let vaultName: VaultName; + const nodeIdGenerator = ids.createNodeIdGenerator(); + const nodeId1 = nodeIdGenerator(); + const nodeId2 = nodeIdGenerator(); + const nodeId3 = nodeIdGenerator(); + const node1: GestaltNodeInfo = { + nodeId: nodeId1, + }; + const node2: GestaltNodeInfo = { + nodeId: nodeId2, + }; + const node3: GestaltNodeInfo = { + nodeId: nodeId3, + }; + // Helper functions + function genVaultName() { + vaultNumber++; + return `vault-${vaultNumber}` as VaultName; + } + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + await polykeyAgent.gestaltGraph.setNode(node1); + await polykeyAgent.gestaltGraph.setNode(node2); + await polykeyAgent.gestaltGraph.setNode(node3); + + vaultNumber = 0; + + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + vaultName = genVaultName(); + command = []; + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('Should get a vaults permissions', async () => { + const vaultId1 = await polykeyAgent.vaultManager.createVault(vaultName); + const vaultId2 = await polykeyAgent.vaultManager.createVault( + vaultName + '1', + ); + const vaultIdEncoded1 = vaultsUtils.encodeVaultId(vaultId1); + const vaultIdEncoded2 = vaultsUtils.encodeVaultId(vaultId2); + const targetNodeId = nodeIdGenerator(); + const targetNodeIdEncoded = nodesUtils.encodeNodeId(targetNodeId); + await polykeyAgent.gestaltGraph.setNode({ + nodeId: targetNodeId, + }); + + // Creating permissions + await polykeyAgent.gestaltGraph.setGestaltAction( + ['node', targetNodeId], + 'scan', + ); + await polykeyAgent.acl.setVaultAction(vaultId1, targetNodeId, 'clone'); + await polykeyAgent.acl.setVaultAction(vaultId1, targetNodeId, 'pull'); + await polykeyAgent.acl.setVaultAction(vaultId2, targetNodeId, 'pull'); + + command = ['vaults', 'permissions', '-np', dataDir, vaultIdEncoded1]; + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + expect(result.stdout).toContain(targetNodeIdEncoded); + expect(result.stdout).toContain('clone'); + expect(result.stdout).toContain('pull'); + + command = ['vaults', 'permissions', '-np', dataDir, vaultIdEncoded2]; + const result2 = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result2.exitCode).toBe(0); + expect(result2.stdout).toContain(targetNodeIdEncoded); + expect(result2.stdout).not.toContain('clone'); + expect(result2.stdout).toContain('pull'); + }); +}); diff --git a/tests/vaults/pullClone.test.ts b/tests/vaults/pullClone.test.ts new file mode 100644 index 00000000..8bca87d7 --- /dev/null +++ b/tests/vaults/pullClone.test.ts @@ -0,0 +1,268 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import type { GestaltNodeInfo } from 'polykey/dist/gestalts/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import * as ids from 'polykey/dist/ids'; +import * as nodesUtils from 'polykey/dist/nodes/utils'; +import * as vaultsUtils from 'polykey/dist/vaults/utils'; +import sysexits from 'polykey/dist/utils/sysexits'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('pull and clone', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let passwordFile: string; + let polykeyAgent: PolykeyAgent; + let command: Array; + let vaultNumber: number; + let vaultName: VaultName; + const nodeIdGenerator = ids.createNodeIdGenerator(); + const nodeId1 = nodeIdGenerator(); + const nodeId2 = nodeIdGenerator(); + const nodeId3 = nodeIdGenerator(); + const node1: GestaltNodeInfo = { + nodeId: nodeId1, + }; + const node2: GestaltNodeInfo = { + nodeId: nodeId2, + }; + const node3: GestaltNodeInfo = { + nodeId: nodeId3, + }; + // Helper functions + function genVaultName() { + vaultNumber++; + return `vault-${vaultNumber}` as VaultName; + } + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + await polykeyAgent.gestaltGraph.setNode(node1); + await polykeyAgent.gestaltGraph.setNode(node2); + await polykeyAgent.gestaltGraph.setNode(node3); + + vaultNumber = 0; + + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + vaultName = genVaultName(); + command = []; + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test( + 'should clone and pull a vault', + async () => { + const dataDir2 = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + const targetPolykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir2, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + const vaultId = + await targetPolykeyAgent.vaultManager.createVault(vaultName); + await targetPolykeyAgent.vaultManager.withVaults( + [vaultId], + async (vault) => { + await vault.writeF(async (efs) => { + await efs.writeFile('secret 1', 'secret the first'); + }); + }, + ); + + await targetPolykeyAgent.gestaltGraph.setNode({ + nodeId: polykeyAgent.keyRing.getNodeId(), + }); + const targetNodeId = targetPolykeyAgent.keyRing.getNodeId(); + const targetNodeIdEncoded = nodesUtils.encodeNodeId(targetNodeId); + await polykeyAgent.nodeManager.setNode( + targetNodeId, + [ + targetPolykeyAgent.agentServiceHost, + targetPolykeyAgent.agentServicePort, + ], + { + mode: 'direct', + connectedTime: Date.now(), + scopes: ['global'], + }, + ); + await targetPolykeyAgent.nodeManager.setNode( + polykeyAgent.keyRing.getNodeId(), + [polykeyAgent.agentServiceHost, polykeyAgent.agentServicePort], + { + mode: 'direct', + connectedTime: Date.now(), + scopes: ['global'], + }, + ); + await polykeyAgent.acl.setNodePerm(targetNodeId, { + gestalt: { + notify: null, + }, + vaults: {}, + }); + + const nodeId = polykeyAgent.keyRing.getNodeId(); + await targetPolykeyAgent.gestaltGraph.setGestaltAction( + ['node', nodeId], + 'scan', + ); + await targetPolykeyAgent.acl.setVaultAction(vaultId, nodeId, 'clone'); + await targetPolykeyAgent.acl.setVaultAction(vaultId, nodeId, 'pull'); + + command = [ + 'vaults', + 'clone', + '-np', + dataDir, + vaultsUtils.encodeVaultId(vaultId), + targetNodeIdEncoded, + ]; + + let result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + + const clonedVaultId = + await polykeyAgent.vaultManager.getVaultId(vaultName); + + await polykeyAgent.vaultManager.withVaults( + [clonedVaultId!], + async (clonedVault) => { + const file = await clonedVault.readF(async (efs) => { + return await efs.readFile('secret 1', { encoding: 'utf8' }); + }); + expect(file).toBe('secret the first'); + }, + ); + + await polykeyAgent.vaultManager.destroyVault(clonedVaultId!); + command = [ + 'vaults', + 'clone', + '-np', + dataDir, + vaultName, + nodesUtils.encodeNodeId(targetNodeId), + ]; + result = await testUtils.pkStdio([...command], { env: {}, cwd: dataDir }); + expect(result.exitCode).toBe(0); + + const secondClonedVaultId = + (await polykeyAgent.vaultManager.getVaultId(vaultName))!; + await polykeyAgent.vaultManager.withVaults( + [secondClonedVaultId!], + async (secondClonedVault) => { + const file = await secondClonedVault.readF(async (efs) => { + return await efs.readFile('secret 1', { encoding: 'utf8' }); + }); + expect(file).toBe('secret the first'); + }, + ); + + await targetPolykeyAgent.vaultManager.withVaults( + [vaultId], + async (vault) => { + await vault.writeF(async (efs) => { + await efs.writeFile('secret 2', 'secret the second'); + }); + }, + ); + + command = ['vaults', 'pull', '-np', dataDir, vaultName]; + result = await testUtils.pkStdio([...command], { env: {}, cwd: dataDir }); + expect(result.exitCode).toBe(0); + + await polykeyAgent.vaultManager.withVaults( + [secondClonedVaultId!], + async (secondClonedVault) => { + const file = await secondClonedVault.readF(async (efs) => { + return await efs.readFile('secret 2', { encoding: 'utf8' }); + }); + expect(file).toBe('secret the second'); + }, + ); + + command = [ + 'vaults', + 'pull', + '-np', + dataDir, + '-pv', + 'InvalidName', + vaultsUtils.encodeVaultId(secondClonedVaultId), + targetNodeIdEncoded, + ]; + result = await testUtils.pkStdio([...command], { env: {}, cwd: dataDir }); + expect(result.exitCode).toBe(sysexits.USAGE); + expect(result.stderr).toContain('ErrorVaultsVaultUndefined'); + + command = [ + 'vaults', + 'pull', + '-np', + dataDir, + '-pv', + vaultName, + vaultsUtils.encodeVaultId(secondClonedVaultId), + 'InvalidNodeId', + ]; + result = await testUtils.pkStdio([...command], { env: {}, cwd: dataDir }); + expect(result.exitCode).toBe(sysexits.USAGE); + + await targetPolykeyAgent.stop(); + await fs.promises.rm(dataDir2, { + force: true, + recursive: true, + }); + }, + globalThis.defaultTimeout * 3, + ); +}); diff --git a/tests/vaults/rename.test.ts b/tests/vaults/rename.test.ts new file mode 100644 index 00000000..655cfc9e --- /dev/null +++ b/tests/vaults/rename.test.ts @@ -0,0 +1,130 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import type { GestaltNodeInfo } from 'polykey/dist/gestalts/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import * as ids from 'polykey/dist/ids'; +import sysexits from 'polykey/dist/utils/sysexits'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandRenameVault', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let passwordFile: string; + let polykeyAgent: PolykeyAgent; + let command: Array; + let vaultNumber: number; + let vaultName: VaultName; + const nodeIdGenerator = ids.createNodeIdGenerator(); + const nodeId1 = nodeIdGenerator(); + const nodeId2 = nodeIdGenerator(); + const nodeId3 = nodeIdGenerator(); + const node1: GestaltNodeInfo = { + nodeId: nodeId1, + }; + const node2: GestaltNodeInfo = { + nodeId: nodeId2, + }; + const node3: GestaltNodeInfo = { + nodeId: nodeId3, + }; + // Helper functions + function genVaultName() { + vaultNumber++; + return `vault-${vaultNumber}` as VaultName; + } + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + await polykeyAgent.gestaltGraph.setNode(node1); + await polykeyAgent.gestaltGraph.setNode(node2); + await polykeyAgent.gestaltGraph.setNode(node3); + + vaultNumber = 0; + + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + vaultName = genVaultName(); + command = []; + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('should rename vault', async () => { + command = ['vaults', 'rename', vaultName, 'RenamedVault', '-np', dataDir]; + await polykeyAgent.vaultManager.createVault(vaultName); + const id = polykeyAgent.vaultManager.getVaultId(vaultName); + expect(id).toBeTruthy(); + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + + const list = (await polykeyAgent.vaultManager.listVaults()).keys(); + const namesList: string[] = []; + for await (const name of list) { + namesList.push(name); + } + expect(namesList).toContain('RenamedVault'); + }); + test('should fail to rename non-existent vault', async () => { + command = [ + 'vaults', + 'rename', + 'z4iAXFwgHGeyUrdC5CiCNU4', // Vault does not exist + 'RenamedVault', + '-np', + dataDir, + ]; + await polykeyAgent.vaultManager.createVault(vaultName); + const id = polykeyAgent.vaultManager.getVaultId(vaultName); + expect(id).toBeTruthy(); + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + // Exit code of the exception + expect(result.exitCode).toBe(sysexits.USAGE); + + const list = (await polykeyAgent.vaultManager.listVaults()).keys(); + const namesList: string[] = []; + for await (const name of list) { + namesList.push(name); + } + expect(namesList).toContain(vaultName); + }); +}); diff --git a/tests/vaults/scanNode.test.ts b/tests/vaults/scanNode.test.ts new file mode 100644 index 00000000..59236dfe --- /dev/null +++ b/tests/vaults/scanNode.test.ts @@ -0,0 +1,197 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import type { GestaltNodeInfo } from 'polykey/dist/gestalts/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import * as ids from 'polykey/dist/ids'; +import * as nodesUtils from 'polykey/dist/nodes/utils'; +import * as vaultsUtils from 'polykey/dist/vaults/utils'; +import sysexits from 'polykey/dist/utils/sysexits'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +// Fixme: temperamental problem with formatting the output. Fails sometimes due to an added space +describe('commandScanNode', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let passwordFile: string; + let polykeyAgent: PolykeyAgent; + const nodeIdGenerator = ids.createNodeIdGenerator(); + const nodeId1 = nodeIdGenerator(); + const nodeId2 = nodeIdGenerator(); + const nodeId3 = nodeIdGenerator(); + const node1: GestaltNodeInfo = { + nodeId: nodeId1, + }; + const node2: GestaltNodeInfo = { + nodeId: nodeId2, + }; + const node3: GestaltNodeInfo = { + nodeId: nodeId3, + }; + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + await polykeyAgent.gestaltGraph.setNode(node1); + await polykeyAgent.gestaltGraph.setNode(node2); + await polykeyAgent.gestaltGraph.setNode(node3); + + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test( + 'should return the vaults names and ids of the remote vault', + async () => { + let remoteOnline: PolykeyAgent | undefined; + try { + remoteOnline = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: path.join(dataDir, 'remoteOnline'), + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger, + }); + const remoteOnlineNodeId = remoteOnline.keyRing.getNodeId(); + const remoteOnlineNodeIdEncoded = + nodesUtils.encodeNodeId(remoteOnlineNodeId); + await polykeyAgent.nodeManager.setNode( + remoteOnlineNodeId, + [remoteOnline.agentServiceHost, remoteOnline.agentServicePort], + { + mode: 'direct', + connectedTime: Date.now(), + scopes: ['global'], + }, + ); + + await remoteOnline.gestaltGraph.setNode({ + nodeId: polykeyAgent.keyRing.getNodeId(), + }); + + const commands1 = [ + 'vaults', + 'scan', + remoteOnlineNodeIdEncoded, + '-np', + dataDir, + ]; + const result1 = await testUtils.pkStdio(commands1, { + env: { PK_PASSWORD: 'password' }, + cwd: dataDir, + }); + expect(result1.exitCode).toEqual(sysexits.NOPERM); + expect(result1.stderr).toContain( + 'ErrorVaultsPermissionDenied: Permission was denied - Scanning is not allowed for', + ); + + await remoteOnline.gestaltGraph.setGestaltAction( + ['node', polykeyAgent.keyRing.getNodeId()], + 'notify', + ); + + const commands2 = [ + 'vaults', + 'scan', + remoteOnlineNodeIdEncoded, + '-np', + dataDir, + ]; + const result2 = await testUtils.pkStdio(commands2, { + env: { PK_PASSWORD: 'password' }, + cwd: dataDir, + }); + expect(result2.exitCode).toEqual(sysexits.NOPERM); + expect(result2.stderr).toContain( + 'ErrorVaultsPermissionDenied: Permission was denied - Scanning is not allowed for', + ); + + await remoteOnline.gestaltGraph.setGestaltAction( + ['node', polykeyAgent.keyRing.getNodeId()], + 'scan', + ); + + const vault1Id = await remoteOnline.vaultManager.createVault( + 'Vault1' as VaultName, + ); + const vault2Id = await remoteOnline.vaultManager.createVault( + 'Vault2' as VaultName, + ); + const vault3Id = await remoteOnline.vaultManager.createVault( + 'Vault3' as VaultName, + ); + const nodeId = polykeyAgent.keyRing.getNodeId(); + await remoteOnline.acl.setVaultAction(vault1Id, nodeId, 'clone'); + await remoteOnline.acl.setVaultAction(vault2Id, nodeId, 'pull'); + await remoteOnline.acl.setVaultAction(vault2Id, nodeId, 'clone'); + const commands3 = [ + 'vaults', + 'scan', + remoteOnlineNodeIdEncoded, + '-np', + dataDir, + ]; + const result3 = await testUtils.pkStdio(commands3, { + env: { PK_PASSWORD: 'password' }, + cwd: dataDir, + }); + expect(result3.exitCode).toBe(0); + expect(result3.stdout).toMatch(/Vault1\t.*\tclone/); + expect(JSON.stringify(result3.stdout)).toContain( + JSON.stringify( + `Vault1\t${vaultsUtils.encodeVaultId( + vault1Id, + )}\tclone\nVault2\t${vaultsUtils.encodeVaultId( + vault2Id, + )}\tpull,clone\n`, + ), + ); + expect(result3.stdout).not.toContain( + `Vault3\t${vaultsUtils.encodeVaultId(vault3Id)}`, + ); + } finally { + await remoteOnline?.stop(); + } + }, + globalThis.defaultTimeout * 2, + ); +}); diff --git a/tests/vaults/share.test.ts b/tests/vaults/share.test.ts new file mode 100644 index 00000000..c7e9f04f --- /dev/null +++ b/tests/vaults/share.test.ts @@ -0,0 +1,129 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import type { GestaltNodeInfo } from 'polykey/dist/gestalts/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import * as ids from 'polykey/dist/ids'; +import * as nodesUtils from 'polykey/dist/nodes/utils'; +import * as vaultsUtils from 'polykey/dist/vaults/utils'; +import NotificationsManager from 'polykey/dist/notifications/NotificationsManager'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandShare', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let passwordFile: string; + let polykeyAgent: PolykeyAgent; + let command: Array; + let vaultNumber: number; + let vaultName: VaultName; + const nodeIdGenerator = ids.createNodeIdGenerator(); + const nodeId1 = nodeIdGenerator(); + const nodeId2 = nodeIdGenerator(); + const nodeId3 = nodeIdGenerator(); + const node1: GestaltNodeInfo = { + nodeId: nodeId1, + }; + const node2: GestaltNodeInfo = { + nodeId: nodeId2, + }; + const node3: GestaltNodeInfo = { + nodeId: nodeId3, + }; + // Helper functions + function genVaultName() { + vaultNumber++; + return `vault-${vaultNumber}` as VaultName; + } + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + await polykeyAgent.gestaltGraph.setNode(node1); + await polykeyAgent.gestaltGraph.setNode(node2); + await polykeyAgent.gestaltGraph.setNode(node3); + + vaultNumber = 0; + + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + vaultName = genVaultName(); + command = []; + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('Should share a vault', async () => { + const mockedSendNotification = jest.spyOn( + NotificationsManager.prototype, + 'sendNotification', + ); + try { + // We don't want to actually send a notification + mockedSendNotification.mockImplementation(async (_) => {}); + const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); + const vaultIdEncoded = vaultsUtils.encodeVaultId(vaultId); + const targetNodeId = nodeIdGenerator(); + const targetNodeIdEncoded = nodesUtils.encodeNodeId(targetNodeId); + await polykeyAgent.gestaltGraph.setNode({ + nodeId: targetNodeId, + }); + expect( + (await polykeyAgent.acl.getNodePerm(targetNodeId))?.vaults[vaultId], + ).toBeUndefined(); + + command = [ + 'vaults', + 'share', + '-np', + dataDir, + vaultIdEncoded, + targetNodeIdEncoded, + ]; + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + + // Check permission + const permissions1 = (await polykeyAgent.acl.getNodePerm(targetNodeId)) + ?.vaults[vaultId]; + expect(permissions1).toBeDefined(); + expect(permissions1.pull).toBeDefined(); + expect(permissions1.clone).toBeDefined(); + } finally { + mockedSendNotification.mockRestore(); + } + }); +}); diff --git a/tests/vaults/unshare.test.ts b/tests/vaults/unshare.test.ts new file mode 100644 index 00000000..67fc5f38 --- /dev/null +++ b/tests/vaults/unshare.test.ts @@ -0,0 +1,159 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import type { GestaltNodeInfo } from 'polykey/dist/gestalts/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import * as ids from 'polykey/dist/ids'; +import * as nodesUtils from 'polykey/dist/nodes/utils'; +import * as vaultsUtils from 'polykey/dist/vaults/utils'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandUnshare', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let passwordFile: string; + let polykeyAgent: PolykeyAgent; + let command: Array; + let vaultNumber: number; + let vaultName: VaultName; + const nodeIdGenerator = ids.createNodeIdGenerator(); + const nodeId1 = nodeIdGenerator(); + const nodeId2 = nodeIdGenerator(); + const nodeId3 = nodeIdGenerator(); + const node1: GestaltNodeInfo = { + nodeId: nodeId1, + }; + const node2: GestaltNodeInfo = { + nodeId: nodeId2, + }; + const node3: GestaltNodeInfo = { + nodeId: nodeId3, + }; + // Helper functions + function genVaultName() { + vaultNumber++; + return `vault-${vaultNumber}` as VaultName; + } + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + await polykeyAgent.gestaltGraph.setNode(node1); + await polykeyAgent.gestaltGraph.setNode(node2); + await polykeyAgent.gestaltGraph.setNode(node3); + + vaultNumber = 0; + + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + vaultName = genVaultName(); + command = []; + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('Should unshare a vault', async () => { + const vaultId1 = await polykeyAgent.vaultManager.createVault(vaultName); + const vaultId2 = await polykeyAgent.vaultManager.createVault( + vaultName + '1', + ); + const vaultIdEncoded1 = vaultsUtils.encodeVaultId(vaultId1); + const vaultIdEncoded2 = vaultsUtils.encodeVaultId(vaultId2); + const targetNodeId = nodeIdGenerator(); + const targetNodeIdEncoded = nodesUtils.encodeNodeId(targetNodeId); + await polykeyAgent.gestaltGraph.setNode({ + nodeId: targetNodeId, + }); + + // Creating permissions + await polykeyAgent.gestaltGraph.setGestaltAction( + ['node', targetNodeId], + 'scan', + ); + await polykeyAgent.acl.setVaultAction(vaultId1, targetNodeId, 'clone'); + await polykeyAgent.acl.setVaultAction(vaultId1, targetNodeId, 'pull'); + await polykeyAgent.acl.setVaultAction(vaultId2, targetNodeId, 'clone'); + await polykeyAgent.acl.setVaultAction(vaultId2, targetNodeId, 'pull'); + + command = [ + 'vaults', + 'unshare', + '-np', + dataDir, + vaultIdEncoded1, + targetNodeIdEncoded, + ]; + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + + // Check permission + const permissions = (await polykeyAgent.acl.getNodePerm(targetNodeId)) + ?.vaults[vaultId1]; + expect(permissions).toBeDefined(); + expect(permissions.pull).toBeUndefined(); + expect(permissions.clone).toBeUndefined(); + + expect( + (await polykeyAgent.acl.getNodePerm(targetNodeId))?.gestalt['scan'], + ).toBeDefined(); + + command = [ + 'vaults', + 'unshare', + '-np', + dataDir, + vaultIdEncoded2, + targetNodeIdEncoded, + ]; + const result2 = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result2.exitCode).toBe(0); + + // Check permission + const permissions2 = (await polykeyAgent.acl.getNodePerm(targetNodeId)) + ?.vaults[vaultId2]; + expect(permissions2).toBeDefined(); + expect(permissions2.pull).toBeUndefined(); + expect(permissions2.clone).toBeUndefined(); + + // And the scan permission should be removed + expect( + (await polykeyAgent.acl.getNodePerm(targetNodeId))?.gestalt['scan'], + ).toBeUndefined(); + }); +}); diff --git a/tests/vaults/vaults.test.ts b/tests/vaults/vaults.test.ts deleted file mode 100644 index d5639d9c..00000000 --- a/tests/vaults/vaults.test.ts +++ /dev/null @@ -1,869 +0,0 @@ -import type { VaultId, VaultName } from 'polykey/dist/vaults/types'; -import type { GestaltNodeInfo } from 'polykey/dist/gestalts/types'; -import path from 'path'; -import fs from 'fs'; -import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; -import PolykeyAgent from 'polykey/dist/PolykeyAgent'; -import * as ids from 'polykey/dist/ids'; -import * as nodesUtils from 'polykey/dist/nodes/utils'; -import * as vaultsUtils from 'polykey/dist/vaults/utils'; -import sysexits from 'polykey/dist/utils/sysexits'; -import NotificationsManager from 'polykey/dist/notifications/NotificationsManager'; -import * as keysUtils from 'polykey/dist/keys/utils'; -import * as testUtils from '../utils'; - -describe('CLI vaults', () => { - const password = 'password'; - const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); - let dataDir: string; - let passwordFile: string; - let polykeyAgent: PolykeyAgent; - let command: Array; - let vaultNumber: number; - let vaultName: VaultName; - const nodeIdGenerator = ids.createNodeIdGenerator(); - const nodeId1 = nodeIdGenerator(); - const nodeId2 = nodeIdGenerator(); - const nodeId3 = nodeIdGenerator(); - const node1: GestaltNodeInfo = { - nodeId: nodeId1, - }; - const node2: GestaltNodeInfo = { - nodeId: nodeId2, - }; - const node3: GestaltNodeInfo = { - nodeId: nodeId3, - }; - // Helper functions - function genVaultName() { - vaultNumber++; - return `vault-${vaultNumber}` as VaultName; - } - beforeEach(async () => { - dataDir = await fs.promises.mkdtemp( - path.join(globalThis.tmpDir, 'polykey-test-'), - ); - passwordFile = path.join(dataDir, 'passwordFile'); - await fs.promises.writeFile(passwordFile, 'password'); - polykeyAgent = await PolykeyAgent.createPolykeyAgent({ - password, - options: { - nodePath: dataDir, - agentServiceHost: '127.0.0.1', - clientServiceHost: '127.0.0.1', - keys: { - passwordOpsLimit: keysUtils.passwordOpsLimits.min, - passwordMemLimit: keysUtils.passwordMemLimits.min, - strictMemoryLock: false, - }, - }, - logger: logger, - }); - await polykeyAgent.gestaltGraph.setNode(node1); - await polykeyAgent.gestaltGraph.setNode(node2); - await polykeyAgent.gestaltGraph.setNode(node3); - - vaultNumber = 0; - - // Authorize session - await testUtils.pkStdio( - ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], - { - env: {}, - cwd: dataDir, - }, - ); - vaultName = genVaultName(); - command = []; - }); - afterEach(async () => { - await polykeyAgent.stop(); - await fs.promises.rm(dataDir, { - force: true, - recursive: true, - }); - }); - - describe('commandListVaults', () => { - test('should list all vaults', async () => { - command = ['vaults', 'list', '-np', dataDir]; - await polykeyAgent.vaultManager.createVault('Vault1' as VaultName); - await polykeyAgent.vaultManager.createVault('Vault2' as VaultName); - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(0); - }); - }); - describe('commandCreateVaults', () => { - test('should create vaults', async () => { - command = ['vaults', 'create', '-np', dataDir, 'MyTestVault']; - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(0); - const result2 = await testUtils.pkStdio( - ['vaults', 'touch', '-np', dataDir, 'MyTestVault2'], - { - env: {}, - cwd: dataDir, - }, - ); - expect(result2.exitCode).toBe(0); - - const list = (await polykeyAgent.vaultManager.listVaults()).keys(); - const namesList: string[] = []; - for await (const name of list) { - namesList.push(name); - } - expect(namesList).toContain('MyTestVault'); - expect(namesList).toContain('MyTestVault2'); - }); - }); - describe('commandRenameVault', () => { - test('should rename vault', async () => { - command = ['vaults', 'rename', vaultName, 'RenamedVault', '-np', dataDir]; - await polykeyAgent.vaultManager.createVault(vaultName); - const id = polykeyAgent.vaultManager.getVaultId(vaultName); - expect(id).toBeTruthy(); - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(0); - - const list = (await polykeyAgent.vaultManager.listVaults()).keys(); - const namesList: string[] = []; - for await (const name of list) { - namesList.push(name); - } - expect(namesList).toContain('RenamedVault'); - }); - test('should fail to rename non-existent vault', async () => { - command = [ - 'vaults', - 'rename', - 'z4iAXFwgHGeyUrdC5CiCNU4', // Vault does not exist - 'RenamedVault', - '-np', - dataDir, - ]; - await polykeyAgent.vaultManager.createVault(vaultName); - const id = polykeyAgent.vaultManager.getVaultId(vaultName); - expect(id).toBeTruthy(); - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - // Exit code of the exception - expect(result.exitCode).toBe(sysexits.USAGE); - - const list = (await polykeyAgent.vaultManager.listVaults()).keys(); - const namesList: string[] = []; - for await (const name of list) { - namesList.push(name); - } - expect(namesList).toContain(vaultName); - }); - }); - describe('commandDeleteVault', () => { - test('should delete vault', async () => { - command = ['vaults', 'delete', '-np', dataDir, vaultName]; - await polykeyAgent.vaultManager.createVault(vaultName); - let id = polykeyAgent.vaultManager.getVaultId(vaultName); - expect(id).toBeTruthy(); - - id = polykeyAgent.vaultManager.getVaultId(vaultName); - expect(id).toBeTruthy(); - - const result2 = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result2.exitCode).toBe(0); - - const list = (await polykeyAgent.vaultManager.listVaults()).keys(); - const namesList: string[] = []; - for await (const name of list) { - namesList.push(name); - } - expect(namesList).not.toContain(vaultName); - }); - }); - test( - 'should clone and pull a vault', - async () => { - const dataDir2 = await fs.promises.mkdtemp( - path.join(globalThis.tmpDir, 'polykey-test-'), - ); - const targetPolykeyAgent = await PolykeyAgent.createPolykeyAgent({ - password, - options: { - nodePath: dataDir2, - agentServiceHost: '127.0.0.1', - clientServiceHost: '127.0.0.1', - keys: { - passwordOpsLimit: keysUtils.passwordOpsLimits.min, - passwordMemLimit: keysUtils.passwordMemLimits.min, - strictMemoryLock: false, - }, - }, - logger: logger, - }); - const vaultId = - await targetPolykeyAgent.vaultManager.createVault(vaultName); - await targetPolykeyAgent.vaultManager.withVaults( - [vaultId], - async (vault) => { - await vault.writeF(async (efs) => { - await efs.writeFile('secret 1', 'secret the first'); - }); - }, - ); - - await targetPolykeyAgent.gestaltGraph.setNode({ - nodeId: polykeyAgent.keyRing.getNodeId(), - }); - const targetNodeId = targetPolykeyAgent.keyRing.getNodeId(); - const targetNodeIdEncoded = nodesUtils.encodeNodeId(targetNodeId); - await polykeyAgent.nodeManager.setNode( - targetNodeId, - [ - targetPolykeyAgent.agentServiceHost, - targetPolykeyAgent.agentServicePort, - ], - { - mode: 'direct', - connectedTime: Date.now(), - scopes: ['global'], - }, - ); - await targetPolykeyAgent.nodeManager.setNode( - polykeyAgent.keyRing.getNodeId(), - [polykeyAgent.agentServiceHost, polykeyAgent.agentServicePort], - { - mode: 'direct', - connectedTime: Date.now(), - scopes: ['global'], - }, - ); - await polykeyAgent.acl.setNodePerm(targetNodeId, { - gestalt: { - notify: null, - }, - vaults: {}, - }); - - const nodeId = polykeyAgent.keyRing.getNodeId(); - await targetPolykeyAgent.gestaltGraph.setGestaltAction( - ['node', nodeId], - 'scan', - ); - await targetPolykeyAgent.acl.setVaultAction(vaultId, nodeId, 'clone'); - await targetPolykeyAgent.acl.setVaultAction(vaultId, nodeId, 'pull'); - - command = [ - 'vaults', - 'clone', - '-np', - dataDir, - vaultsUtils.encodeVaultId(vaultId), - targetNodeIdEncoded, - ]; - - let result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(0); - - const clonedVaultId = - await polykeyAgent.vaultManager.getVaultId(vaultName); - - await polykeyAgent.vaultManager.withVaults( - [clonedVaultId!], - async (clonedVault) => { - const file = await clonedVault.readF(async (efs) => { - return await efs.readFile('secret 1', { encoding: 'utf8' }); - }); - expect(file).toBe('secret the first'); - }, - ); - - await polykeyAgent.vaultManager.destroyVault(clonedVaultId!); - command = [ - 'vaults', - 'clone', - '-np', - dataDir, - vaultName, - nodesUtils.encodeNodeId(targetNodeId), - ]; - result = await testUtils.pkStdio([...command], { env: {}, cwd: dataDir }); - expect(result.exitCode).toBe(0); - - const secondClonedVaultId = - (await polykeyAgent.vaultManager.getVaultId(vaultName))!; - await polykeyAgent.vaultManager.withVaults( - [secondClonedVaultId!], - async (secondClonedVault) => { - const file = await secondClonedVault.readF(async (efs) => { - return await efs.readFile('secret 1', { encoding: 'utf8' }); - }); - expect(file).toBe('secret the first'); - }, - ); - - await targetPolykeyAgent.vaultManager.withVaults( - [vaultId], - async (vault) => { - await vault.writeF(async (efs) => { - await efs.writeFile('secret 2', 'secret the second'); - }); - }, - ); - - command = ['vaults', 'pull', '-np', dataDir, vaultName]; - result = await testUtils.pkStdio([...command], { env: {}, cwd: dataDir }); - expect(result.exitCode).toBe(0); - - await polykeyAgent.vaultManager.withVaults( - [secondClonedVaultId!], - async (secondClonedVault) => { - const file = await secondClonedVault.readF(async (efs) => { - return await efs.readFile('secret 2', { encoding: 'utf8' }); - }); - expect(file).toBe('secret the second'); - }, - ); - - command = [ - 'vaults', - 'pull', - '-np', - dataDir, - '-pv', - 'InvalidName', - vaultsUtils.encodeVaultId(secondClonedVaultId), - targetNodeIdEncoded, - ]; - result = await testUtils.pkStdio([...command], { env: {}, cwd: dataDir }); - expect(result.exitCode).toBe(sysexits.USAGE); - expect(result.stderr).toContain('ErrorVaultsVaultUndefined'); - - command = [ - 'vaults', - 'pull', - '-np', - dataDir, - '-pv', - vaultName, - vaultsUtils.encodeVaultId(secondClonedVaultId), - 'InvalidNodeId', - ]; - result = await testUtils.pkStdio([...command], { env: {}, cwd: dataDir }); - expect(result.exitCode).toBe(sysexits.USAGE); - - await targetPolykeyAgent.stop(); - await fs.promises.rm(dataDir2, { - force: true, - recursive: true, - }); - }, - globalThis.defaultTimeout * 3, - ); - describe('commandShare', () => { - test('Should share a vault', async () => { - const mockedSendNotification = jest.spyOn( - NotificationsManager.prototype, - 'sendNotification', - ); - try { - // We don't want to actually send a notification - mockedSendNotification.mockImplementation(async (_) => {}); - const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); - const vaultIdEncoded = vaultsUtils.encodeVaultId(vaultId); - const targetNodeId = nodeIdGenerator(); - const targetNodeIdEncoded = nodesUtils.encodeNodeId(targetNodeId); - await polykeyAgent.gestaltGraph.setNode({ - nodeId: targetNodeId, - }); - expect( - (await polykeyAgent.acl.getNodePerm(targetNodeId))?.vaults[vaultId], - ).toBeUndefined(); - - command = [ - 'vaults', - 'share', - '-np', - dataDir, - vaultIdEncoded, - targetNodeIdEncoded, - ]; - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(0); - - // Check permission - const permissions1 = (await polykeyAgent.acl.getNodePerm(targetNodeId)) - ?.vaults[vaultId]; - expect(permissions1).toBeDefined(); - expect(permissions1.pull).toBeDefined(); - expect(permissions1.clone).toBeDefined(); - } finally { - mockedSendNotification.mockRestore(); - } - }); - }); - describe('commandUnshare', () => { - test('Should unshare a vault', async () => { - const vaultId1 = await polykeyAgent.vaultManager.createVault(vaultName); - const vaultId2 = await polykeyAgent.vaultManager.createVault( - vaultName + '1', - ); - const vaultIdEncoded1 = vaultsUtils.encodeVaultId(vaultId1); - const vaultIdEncoded2 = vaultsUtils.encodeVaultId(vaultId2); - const targetNodeId = nodeIdGenerator(); - const targetNodeIdEncoded = nodesUtils.encodeNodeId(targetNodeId); - await polykeyAgent.gestaltGraph.setNode({ - nodeId: targetNodeId, - }); - - // Creating permissions - await polykeyAgent.gestaltGraph.setGestaltAction( - ['node', targetNodeId], - 'scan', - ); - await polykeyAgent.acl.setVaultAction(vaultId1, targetNodeId, 'clone'); - await polykeyAgent.acl.setVaultAction(vaultId1, targetNodeId, 'pull'); - await polykeyAgent.acl.setVaultAction(vaultId2, targetNodeId, 'clone'); - await polykeyAgent.acl.setVaultAction(vaultId2, targetNodeId, 'pull'); - - command = [ - 'vaults', - 'unshare', - '-np', - dataDir, - vaultIdEncoded1, - targetNodeIdEncoded, - ]; - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(0); - - // Check permission - const permissions = (await polykeyAgent.acl.getNodePerm(targetNodeId)) - ?.vaults[vaultId1]; - expect(permissions).toBeDefined(); - expect(permissions.pull).toBeUndefined(); - expect(permissions.clone).toBeUndefined(); - - expect( - (await polykeyAgent.acl.getNodePerm(targetNodeId))?.gestalt['scan'], - ).toBeDefined(); - - command = [ - 'vaults', - 'unshare', - '-np', - dataDir, - vaultIdEncoded2, - targetNodeIdEncoded, - ]; - const result2 = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result2.exitCode).toBe(0); - - // Check permission - const permissions2 = (await polykeyAgent.acl.getNodePerm(targetNodeId)) - ?.vaults[vaultId2]; - expect(permissions2).toBeDefined(); - expect(permissions2.pull).toBeUndefined(); - expect(permissions2.clone).toBeUndefined(); - - // And the scan permission should be removed - expect( - (await polykeyAgent.acl.getNodePerm(targetNodeId))?.gestalt['scan'], - ).toBeUndefined(); - }); - }); - describe('commandPermissions', () => { - test('Should get a vaults permissions', async () => { - const vaultId1 = await polykeyAgent.vaultManager.createVault(vaultName); - const vaultId2 = await polykeyAgent.vaultManager.createVault( - vaultName + '1', - ); - const vaultIdEncoded1 = vaultsUtils.encodeVaultId(vaultId1); - const vaultIdEncoded2 = vaultsUtils.encodeVaultId(vaultId2); - const targetNodeId = nodeIdGenerator(); - const targetNodeIdEncoded = nodesUtils.encodeNodeId(targetNodeId); - await polykeyAgent.gestaltGraph.setNode({ - nodeId: targetNodeId, - }); - - // Creating permissions - await polykeyAgent.gestaltGraph.setGestaltAction( - ['node', targetNodeId], - 'scan', - ); - await polykeyAgent.acl.setVaultAction(vaultId1, targetNodeId, 'clone'); - await polykeyAgent.acl.setVaultAction(vaultId1, targetNodeId, 'pull'); - await polykeyAgent.acl.setVaultAction(vaultId2, targetNodeId, 'pull'); - - command = ['vaults', 'permissions', '-np', dataDir, vaultIdEncoded1]; - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(0); - expect(result.stdout).toContain(targetNodeIdEncoded); - expect(result.stdout).toContain('clone'); - expect(result.stdout).toContain('pull'); - - command = ['vaults', 'permissions', '-np', dataDir, vaultIdEncoded2]; - const result2 = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result2.exitCode).toBe(0); - expect(result2.stdout).toContain(targetNodeIdEncoded); - expect(result2.stdout).not.toContain('clone'); - expect(result2.stdout).toContain('pull'); - }); - }); - describe('commandVaultVersion', () => { - test('should switch the version of a vault', async () => { - const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); - const id = polykeyAgent.vaultManager.getVaultId(vaultName); - expect(id).toBeTruthy(); - - const secret1 = { name: 'Secret-1', content: 'Secret-1-content' }; - const secret2 = { name: 'Secret-1', content: 'Secret-2-content' }; - - const ver1Oid = await polykeyAgent.vaultManager.withVaults( - [vaultId], - async (vault) => { - await vault.writeF(async (efs) => { - await efs.writeFile(secret1.name, secret1.content); - }); - const ver1Oid = (await vault.log(undefined, 1))[0].commitId; - - await vault.writeF(async (efs) => { - await efs.writeFile(secret2.name, secret2.content); - }); - return ver1Oid; - }, - ); - - const command = ['vaults', 'version', '-np', dataDir, vaultName, ver1Oid]; - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(0); - - await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - const fileContents = await vault.readF(async (efs) => { - return (await efs.readFile(secret1.name)).toString(); - }); - expect(fileContents).toStrictEqual(secret1.content); - }); - }); - test('should switch the version of a vault to the latest version', async () => { - const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); - const id = polykeyAgent.vaultManager.getVaultId(vaultName); - expect(id).toBeTruthy(); - - const secret1 = { name: 'Secret-1', content: 'Secret-1-content' }; - const secret2 = { name: 'Secret-1', content: 'Secret-2-content' }; - - const ver1Oid = await polykeyAgent.vaultManager.withVaults( - [vaultId], - async (vault) => { - await vault.writeF(async (efs) => { - await efs.writeFile(secret1.name, secret1.content); - }); - const ver1Oid = (await vault.log(undefined, 1))[0].commitId; - - await vault.writeF(async (efs) => { - await efs.writeFile(secret2.name, secret2.content); - }); - return ver1Oid; - }, - ); - - const command = ['vaults', 'version', '-np', dataDir, vaultName, ver1Oid]; - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(0); - - const command2 = ['vaults', 'version', '-np', dataDir, vaultName, 'last']; - - const result2 = await testUtils.pkStdio([...command2], { - env: {}, - cwd: dataDir, - }); - expect(result2.exitCode).toBe(0); - }); - test('should handle invalid version IDs', async () => { - await polykeyAgent.vaultManager.createVault(vaultName); - const id = polykeyAgent.vaultManager.getVaultId(vaultName); - expect(id).toBeTruthy(); - - const command = [ - 'vaults', - 'version', - '-np', - dataDir, - vaultName, - 'NOT_A_VALID_CHECKOUT_ID', - ]; - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(sysexits.USAGE); - - expect(result.stderr).toContain('ErrorVaultReferenceInvalid'); - }); - test('should throw an error if the vault is not found', async () => { - const command = [ - 'vaults', - 'version', - '-np', - dataDir, - 'zLnM7puKobbh4YXEz66StAq', - 'NOT_A_VALID_CHECKOUT_ID', - ]; - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toBe(sysexits.USAGE); - expect(result.stderr).toContain('ErrorVaultsVaultUndefined'); - }); - }); - describe('commandVaultLog', () => { - const secret1 = { name: 'secret1', content: 'Secret-1-content' }; - const secret2 = { name: 'secret2', content: 'Secret-2-content' }; - - let vaultId: VaultId; - let writeF1Oid: string; - let writeF2Oid: string; - let writeF3Oid: string; - - beforeEach(async () => { - vaultId = await polykeyAgent.vaultManager.createVault(vaultName); - - await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { - await vault.writeF(async (efs) => { - await efs.writeFile(secret1.name, secret1.content); - }); - writeF1Oid = (await vault.log(undefined, 0))[0].commitId; - - await vault.writeF(async (efs) => { - await efs.writeFile(secret2.name, secret2.content); - }); - writeF2Oid = (await vault.log(undefined, 0))[0].commitId; - - await vault.writeF(async (efs) => { - await efs.unlink(secret2.name); - }); - writeF3Oid = (await vault.log(undefined, 0))[0].commitId; - }); - }); - afterEach(async () => { - await polykeyAgent.vaultManager.destroyVault(vaultId); - }); - - test('Should get all writeFs', async () => { - const command = ['vaults', 'log', '-np', dataDir, vaultName]; - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toEqual(0); - expect(result.stdout).toContain(writeF1Oid); - expect(result.stdout).toContain(writeF2Oid); - expect(result.stdout).toContain(writeF3Oid); - }); - test('should get a part of the log', async () => { - const command = ['vaults', 'log', '-np', dataDir, '-d', '2', vaultName]; - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toEqual(0); - expect(result.stdout).not.toContain(writeF1Oid); - expect(result.stdout).toContain(writeF2Oid); - expect(result.stdout).toContain(writeF3Oid); - }); - test('should get a specific writeF', async () => { - const command = [ - 'vaults', - 'log', - '-np', - dataDir, - '-d', - '1', - vaultName, - '-ci', - writeF2Oid, - ]; - - const result = await testUtils.pkStdio([...command], { - env: {}, - cwd: dataDir, - }); - expect(result.exitCode).toEqual(0); - expect(result.stdout).not.toContain(writeF1Oid); - expect(result.stdout).toContain(writeF2Oid); - expect(result.stdout).not.toContain(writeF3Oid); - }); - test.todo('test formatting of the output'); - }); - // Fixme: temperamental problem with formatting the output. Fails sometimes due to an added space - describe('commandScanNode', () => { - test( - 'should return the vaults names and ids of the remote vault', - async () => { - let remoteOnline: PolykeyAgent | undefined; - try { - remoteOnline = await PolykeyAgent.createPolykeyAgent({ - password, - options: { - nodePath: path.join(dataDir, 'remoteOnline'), - agentServiceHost: '127.0.0.1', - clientServiceHost: '127.0.0.1', - keys: { - passwordOpsLimit: keysUtils.passwordOpsLimits.min, - passwordMemLimit: keysUtils.passwordMemLimits.min, - strictMemoryLock: false, - }, - }, - logger, - }); - const remoteOnlineNodeId = remoteOnline.keyRing.getNodeId(); - const remoteOnlineNodeIdEncoded = - nodesUtils.encodeNodeId(remoteOnlineNodeId); - await polykeyAgent.nodeManager.setNode( - remoteOnlineNodeId, - [remoteOnline.agentServiceHost, remoteOnline.agentServicePort], - { - mode: 'direct', - connectedTime: Date.now(), - scopes: ['global'], - }, - ); - - await remoteOnline.gestaltGraph.setNode({ - nodeId: polykeyAgent.keyRing.getNodeId(), - }); - - const commands1 = [ - 'vaults', - 'scan', - remoteOnlineNodeIdEncoded, - '-np', - dataDir, - ]; - const result1 = await testUtils.pkStdio(commands1, { - env: { PK_PASSWORD: 'password' }, - cwd: dataDir, - }); - expect(result1.exitCode).toEqual(sysexits.NOPERM); - expect(result1.stderr).toContain( - 'ErrorVaultsPermissionDenied: Permission was denied - Scanning is not allowed for', - ); - - await remoteOnline.gestaltGraph.setGestaltAction( - ['node', polykeyAgent.keyRing.getNodeId()], - 'notify', - ); - - const commands2 = [ - 'vaults', - 'scan', - remoteOnlineNodeIdEncoded, - '-np', - dataDir, - ]; - const result2 = await testUtils.pkStdio(commands2, { - env: { PK_PASSWORD: 'password' }, - cwd: dataDir, - }); - expect(result2.exitCode).toEqual(sysexits.NOPERM); - expect(result2.stderr).toContain( - 'ErrorVaultsPermissionDenied: Permission was denied - Scanning is not allowed for', - ); - - await remoteOnline.gestaltGraph.setGestaltAction( - ['node', polykeyAgent.keyRing.getNodeId()], - 'scan', - ); - - const vault1Id = await remoteOnline.vaultManager.createVault( - 'Vault1' as VaultName, - ); - const vault2Id = await remoteOnline.vaultManager.createVault( - 'Vault2' as VaultName, - ); - const vault3Id = await remoteOnline.vaultManager.createVault( - 'Vault3' as VaultName, - ); - const nodeId = polykeyAgent.keyRing.getNodeId(); - await remoteOnline.acl.setVaultAction(vault1Id, nodeId, 'clone'); - await remoteOnline.acl.setVaultAction(vault2Id, nodeId, 'pull'); - await remoteOnline.acl.setVaultAction(vault2Id, nodeId, 'clone'); - const commands3 = [ - 'vaults', - 'scan', - remoteOnlineNodeIdEncoded, - '-np', - dataDir, - ]; - const result3 = await testUtils.pkStdio(commands3, { - env: { PK_PASSWORD: 'password' }, - cwd: dataDir, - }); - expect(result3.exitCode).toBe(0); - expect(result3.stdout).toMatch(/Vault1\t.*\tclone/); - expect(JSON.stringify(result3.stdout)).toContain( - JSON.stringify( - `Vault1\t${vaultsUtils.encodeVaultId( - vault1Id, - )}\tclone\nVault2\t${vaultsUtils.encodeVaultId( - vault2Id, - )}\tpull,clone\n`, - ), - ); - expect(result3.stdout).not.toContain( - `Vault3\t${vaultsUtils.encodeVaultId(vault3Id)}`, - ); - } finally { - await remoteOnline?.stop(); - } - }, - globalThis.defaultTimeout * 2, - ); - }); -}); diff --git a/tests/vaults/version.test.ts b/tests/vaults/version.test.ts new file mode 100644 index 00000000..73b77da0 --- /dev/null +++ b/tests/vaults/version.test.ts @@ -0,0 +1,198 @@ +import type { VaultName } from 'polykey/dist/vaults/types'; +import type { GestaltNodeInfo } from 'polykey/dist/gestalts/types'; +import path from 'path'; +import fs from 'fs'; +import Logger, { LogLevel, StreamHandler } from '@matrixai/logger'; +import PolykeyAgent from 'polykey/dist/PolykeyAgent'; +import * as ids from 'polykey/dist/ids'; +import sysexits from 'polykey/dist/utils/sysexits'; +import * as keysUtils from 'polykey/dist/keys/utils'; +import * as testUtils from '../utils'; + +describe('commandVaultVersion', () => { + const password = 'password'; + const logger = new Logger('CLI Test', LogLevel.WARN, [new StreamHandler()]); + let dataDir: string; + let passwordFile: string; + let polykeyAgent: PolykeyAgent; + let vaultNumber: number; + let vaultName: VaultName; + const nodeIdGenerator = ids.createNodeIdGenerator(); + const nodeId1 = nodeIdGenerator(); + const nodeId2 = nodeIdGenerator(); + const nodeId3 = nodeIdGenerator(); + const node1: GestaltNodeInfo = { + nodeId: nodeId1, + }; + const node2: GestaltNodeInfo = { + nodeId: nodeId2, + }; + const node3: GestaltNodeInfo = { + nodeId: nodeId3, + }; + // Helper functions + function genVaultName() { + vaultNumber++; + return `vault-${vaultNumber}` as VaultName; + } + beforeEach(async () => { + dataDir = await fs.promises.mkdtemp( + path.join(globalThis.tmpDir, 'polykey-test-'), + ); + passwordFile = path.join(dataDir, 'passwordFile'); + await fs.promises.writeFile(passwordFile, 'password'); + polykeyAgent = await PolykeyAgent.createPolykeyAgent({ + password, + options: { + nodePath: dataDir, + agentServiceHost: '127.0.0.1', + clientServiceHost: '127.0.0.1', + keys: { + passwordOpsLimit: keysUtils.passwordOpsLimits.min, + passwordMemLimit: keysUtils.passwordMemLimits.min, + strictMemoryLock: false, + }, + }, + logger: logger, + }); + await polykeyAgent.gestaltGraph.setNode(node1); + await polykeyAgent.gestaltGraph.setNode(node2); + await polykeyAgent.gestaltGraph.setNode(node3); + + vaultNumber = 0; + + // Authorize session + await testUtils.pkStdio( + ['agent', 'unlock', '-np', dataDir, '--password-file', passwordFile], + { + env: {}, + cwd: dataDir, + }, + ); + vaultName = genVaultName(); + }); + afterEach(async () => { + await polykeyAgent.stop(); + await fs.promises.rm(dataDir, { + force: true, + recursive: true, + }); + }); + + test('should switch the version of a vault', async () => { + const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); + const id = polykeyAgent.vaultManager.getVaultId(vaultName); + expect(id).toBeTruthy(); + + const secret1 = { name: 'Secret-1', content: 'Secret-1-content' }; + const secret2 = { name: 'Secret-1', content: 'Secret-2-content' }; + + const ver1Oid = await polykeyAgent.vaultManager.withVaults( + [vaultId], + async (vault) => { + await vault.writeF(async (efs) => { + await efs.writeFile(secret1.name, secret1.content); + }); + const ver1Oid = (await vault.log(undefined, 1))[0].commitId; + + await vault.writeF(async (efs) => { + await efs.writeFile(secret2.name, secret2.content); + }); + return ver1Oid; + }, + ); + + const command = ['vaults', 'version', '-np', dataDir, vaultName, ver1Oid]; + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + + await polykeyAgent.vaultManager.withVaults([vaultId], async (vault) => { + const fileContents = await vault.readF(async (efs) => { + return (await efs.readFile(secret1.name)).toString(); + }); + expect(fileContents).toStrictEqual(secret1.content); + }); + }); + test('should switch the version of a vault to the latest version', async () => { + const vaultId = await polykeyAgent.vaultManager.createVault(vaultName); + const id = polykeyAgent.vaultManager.getVaultId(vaultName); + expect(id).toBeTruthy(); + + const secret1 = { name: 'Secret-1', content: 'Secret-1-content' }; + const secret2 = { name: 'Secret-1', content: 'Secret-2-content' }; + + const ver1Oid = await polykeyAgent.vaultManager.withVaults( + [vaultId], + async (vault) => { + await vault.writeF(async (efs) => { + await efs.writeFile(secret1.name, secret1.content); + }); + const ver1Oid = (await vault.log(undefined, 1))[0].commitId; + + await vault.writeF(async (efs) => { + await efs.writeFile(secret2.name, secret2.content); + }); + return ver1Oid; + }, + ); + + const command = ['vaults', 'version', '-np', dataDir, vaultName, ver1Oid]; + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(0); + + const command2 = ['vaults', 'version', '-np', dataDir, vaultName, 'last']; + + const result2 = await testUtils.pkStdio([...command2], { + env: {}, + cwd: dataDir, + }); + expect(result2.exitCode).toBe(0); + }); + test('should handle invalid version IDs', async () => { + await polykeyAgent.vaultManager.createVault(vaultName); + const id = polykeyAgent.vaultManager.getVaultId(vaultName); + expect(id).toBeTruthy(); + + const command = [ + 'vaults', + 'version', + '-np', + dataDir, + vaultName, + 'NOT_A_VALID_CHECKOUT_ID', + ]; + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(sysexits.USAGE); + + expect(result.stderr).toContain('ErrorVaultReferenceInvalid'); + }); + test('should throw an error if the vault is not found', async () => { + const command = [ + 'vaults', + 'version', + '-np', + dataDir, + 'zLnM7puKobbh4YXEz66StAq', + 'NOT_A_VALID_CHECKOUT_ID', + ]; + + const result = await testUtils.pkStdio([...command], { + env: {}, + cwd: dataDir, + }); + expect(result.exitCode).toBe(sysexits.USAGE); + expect(result.stderr).toContain('ErrorVaultsVaultUndefined'); + }); +});