From ede1fb34a3d03c619e368f809c0227e51f875687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gergely=20B=C3=A9k=C3=A9si?= Date: Thu, 28 May 2026 10:55:47 +0200 Subject: [PATCH] feat: add access list command --- src/command/access/index.ts | 3 +- src/command/access/list.ts | 45 +++++++++++++++++++ test/command/access.spec.ts | 88 ++++++++++++++----------------------- test/utility/address.ts | 7 +++ 4 files changed, 87 insertions(+), 56 deletions(-) create mode 100644 src/command/access/list.ts diff --git a/src/command/access/index.ts b/src/command/access/index.ts index 834a32a6..f9444d77 100644 --- a/src/command/access/index.ts +++ b/src/command/access/index.ts @@ -1,6 +1,7 @@ import { GroupCommand } from 'furious-commander' import { Grant } from './grant' import { Init } from './init' +import { List } from './list' import { Revoke } from './revoke' export class Access implements GroupCommand { @@ -8,5 +9,5 @@ export class Access implements GroupCommand { public readonly description = 'Share access to your uploaded files/folders' - public subCommandClasses = [Init, Grant, Revoke] + public subCommandClasses = [Init, Grant, Revoke, List] } diff --git a/src/command/access/list.ts b/src/command/access/list.ts new file mode 100644 index 00000000..93dd1e22 --- /dev/null +++ b/src/command/access/list.ts @@ -0,0 +1,45 @@ +import { LeafCommand, Option } from 'furious-commander' +import { exit } from 'process' +import { AccessHistory } from '../../service/access' +import { errorText } from '../../utils/text' +import { AccessCommand } from './access-command' + +export class List extends AccessCommand implements LeafCommand { + public readonly name = 'list' + + public readonly description = 'List grantees of an existing grantee list' + + @Option({ + key: 'list-name', + alias: 'n', + description: 'Name of the grantee list', + required: true, + type: 'string', + }) + public listName!: string + + public async run(): Promise { + super.init() + + const accessHistory = new AccessHistory(this.commandConfig, this.console) + const lastHistoryEvent = accessHistory.getEvents(this.listName).sort((a, b) => b.createdAt - a.createdAt)[0] + + if (!lastHistoryEvent) { + this.console.error(errorText(`Grantee list with name '${this.listName}' does not exist!`)) + + exit(1) + } + + const response = await this.bee.getGrantees(lastHistoryEvent.granteeListRef) + + if (response.grantees.length === 0) { + this.console.log(`Grantee list '${this.listName}' has no grantees.`) + + return + } + this.console.log(`Grantees of list '${this.listName}':`) + for (const grantee of response.grantees) { + this.console.log(grantee.toCompressedHex()) + } + } +} diff --git a/test/command/access.spec.ts b/test/command/access.spec.ts index b0d3c013..66654e37 100644 --- a/test/command/access.spec.ts +++ b/test/command/access.spec.ts @@ -1,7 +1,7 @@ import { System } from 'cafe-utility' import { existsSync, unlinkSync } from 'fs' import { describeCommand, invokeTestCli } from '../utility' -import { getPssAddress } from '../utility/address' +import { getPublicAddress } from '../utility/address' import { getStampOption } from '../utility/stamp' describeCommand( @@ -16,16 +16,8 @@ describeCommand( }) describe('init', () => { it('should initialize access with pss address as grantee', async () => { - const pssAddress = await getPssAddress('http://localhost:21633') - await invokeTestCli([ - 'access', - 'init', - ...getStampOption(), - '--list-name', - 'test-access', - '--grantee', - pssAddress.toHex(), - ]) + const pubKey = await getPublicAddress('http://localhost:21633') + await invokeTestCli(['access', 'init', ...getStampOption(), '--list-name', 'test-access', '--grantee', pubKey]) expect(getLastMessage()).toEqual("Grantee list 'test-access' initialized successfully!") }) @@ -53,9 +45,9 @@ describeCommand( it('should grant access to a new grantee', async () => { await invokeTestCli(['access', 'init', ...getStampOption(), '-n', 'test-access']) await System.sleepMillis(1000) - const pssAddress = await getPssAddress('http://localhost:21633') - await invokeTestCli(['access', 'grant', '--list-name', 'test-access', '--grantee', pssAddress.toHex()]) - expect(getLastMessage()).toContain(`Access granted to ${pssAddress.toHex()}!`) + const pubKey = await getPublicAddress('http://localhost:21633') + await invokeTestCli(['access', 'grant', '--list-name', 'test-access', '--grantee', pubKey]) + expect(getLastMessage()).toContain(`Access granted to ${pubKey}!`) }) describe('when grantee list does not exist', () => { @@ -80,16 +72,8 @@ describeCommand( it('should show grantee list reference and history address', async () => { await invokeTestCli(['access', 'init', ...getStampOption(), '-n', 'test-access']) await System.sleepMillis(1000) - const pssAddress = await getPssAddress('http://localhost:21633') - await invokeTestCli([ - 'access', - 'grant', - '--list-name', - 'test-access', - '--grantee', - pssAddress.toHex(), - '--verbose', - ]) + const pubKey = await getPublicAddress('http://localhost:21633') + await invokeTestCli(['access', 'grant', '--list-name', 'test-access', '--grantee', pubKey, '--verbose']) expect(getNthLastMessage(2)).toContain('Grantee list reference') expect(getNthLastMessage(2)).toMatch(/[a-f0-9]{64}/g) expect(getLastMessage()).toContain('History address') @@ -100,19 +84,11 @@ describeCommand( describe('revoke', () => { it('should revoke access from a grantee', async () => { - const pssAddress = await getPssAddress('http://localhost:21633') - await invokeTestCli([ - 'access', - 'init', - ...getStampOption(), - '-n', - 'test-access', - '--grantee', - pssAddress.toHex(), - ]) + const pubKey = await getPublicAddress('http://localhost:21633') + await invokeTestCli(['access', 'init', ...getStampOption(), '-n', 'test-access', '--grantee', pubKey]) await System.sleepMillis(1000) - await invokeTestCli(['access', 'revoke', '--list-name', 'test-access', '--grantee', pssAddress.toHex()]) - expect(getLastMessage()).toContain(`Access revoked from ${pssAddress.toHex()}!`) + await invokeTestCli(['access', 'revoke', '--list-name', 'test-access', '--grantee', pubKey]) + expect(getLastMessage()).toContain(`Access revoked from ${pubKey}!`) }) describe('when grantee list does not exist', () => { @@ -135,26 +111,10 @@ describeCommand( describe('when verbose option is used', () => { it('should show grantee list reference and history address', async () => { - const pssAddress = await getPssAddress('http://localhost:21633') - await invokeTestCli([ - 'access', - 'init', - ...getStampOption(), - '-n', - 'test-access', - '--grantee', - pssAddress.toHex(), - ]) + const pubKey = await getPublicAddress('http://localhost:21633') + await invokeTestCli(['access', 'init', ...getStampOption(), '-n', 'test-access', '--grantee', pubKey]) await System.sleepMillis(1000) - await invokeTestCli([ - 'access', - 'revoke', - '--list-name', - 'test-access', - '--grantee', - pssAddress.toHex(), - '--verbose', - ]) + await invokeTestCli(['access', 'revoke', '--list-name', 'test-access', '--grantee', pubKey, '--verbose']) expect(getNthLastMessage(2)).toContain('Grantee list reference') expect(getNthLastMessage(2)).toMatch(/[a-f0-9]{64}/g) expect(getLastMessage()).toContain('History address') @@ -162,6 +122,24 @@ describeCommand( }) }) }) + + describe('list', () => { + it('should list all grantees in the list', async () => { + const granteePubKey = await getPublicAddress('http://localhost:21633') + await invokeTestCli(['access', 'init', ...getStampOption(), '-n', 'test-access']) + await System.sleepMillis(1000) + await invokeTestCli(['access', 'grant', '--list-name', 'test-access', '--grantee', granteePubKey]) + await System.sleepMillis(1000) + await invokeTestCli(['access', 'list', '--list-name', 'test-access']) + expect(getNthLastMessage(2)).toContain(`Grantees of list 'test-access':`) + expect(getLastMessage()).toContain(granteePubKey) + + await invokeTestCli(['access', 'revoke', '--list-name', 'test-access', '--grantee', granteePubKey]) + await System.sleepMillis(1000) + await invokeTestCli(['access', 'list', '--list-name', 'test-access']) + expect(getLastMessage()).toContain("Grantee list 'test-access' has no grantees.") + }) + }) }, { configFileName: 'access' }, ) diff --git a/test/utility/address.ts b/test/utility/address.ts index d219884c..e2e3d304 100644 --- a/test/utility/address.ts +++ b/test/utility/address.ts @@ -8,6 +8,13 @@ export async function getPssAddress(beeApiUrl: string): Promise { return (execution.runnable as Addresses).nodeAddresses.pssPublicKey } +export async function getPublicAddress(beeApiUrl: string): Promise { + const response = await fetch(beeApiUrl + '/addresses') + const data = await response.json() + + return data.publicKey +} + export function getWorkerPssAddress(stringLength: number): string { if (!process.env.WORKER_PSS_ADDRESS) { throw Error('Worker PSS address is not set.')