From 53f9454b6ec7495d2cfc0e2e9f34ccb0845c8a39 Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Thu, 18 Jun 2020 12:25:02 +0300 Subject: [PATCH] feat: Authorized agent methods --- packages/daf-core/src/__tests__/agent.test.ts | 101 ++++++++++++++++++ .../daf-core/src/__tests__/default.test.ts | 6 -- packages/daf-core/src/agent.ts | 33 +++++- 3 files changed, 131 insertions(+), 9 deletions(-) create mode 100644 packages/daf-core/src/__tests__/agent.test.ts delete mode 100644 packages/daf-core/src/__tests__/default.test.ts diff --git a/packages/daf-core/src/__tests__/agent.test.ts b/packages/daf-core/src/__tests__/agent.test.ts new file mode 100644 index 000000000..41702404c --- /dev/null +++ b/packages/daf-core/src/__tests__/agent.test.ts @@ -0,0 +1,101 @@ +import { Agent } from '../agent' +import { IAgentPlugin } from '../types' + +describe('daf-core agent', () => { + it('should use plugin methods', async () => { + const plugin: IAgentPlugin = { + methods: { + doSomething: jest.fn(), + }, + } + const agent = new Agent({ + plugins: [plugin], + }) + + //@ts-ignore + await agent.doSomething({ foo: 'baz' }) + expect(plugin.methods.doSomething).toBeCalledWith({ foo: 'baz' }, { agent }) + await agent.execute('doSomething', { foo: 'bar' }) + expect(plugin.methods.doSomething).toBeCalledWith({ foo: 'bar' }, { agent }) + }) + + it('should allow method overrides', async () => { + const doSomething = jest.fn() + const plugin: IAgentPlugin = { + methods: { + doSomething: jest.fn(), + }, + } + const agent = new Agent({ + plugins: [plugin], + overrides: { + doSomething, + }, + }) + + //@ts-ignore + await agent.doSomething({ foo: 'baz' }) + expect(doSomething).toBeCalledWith({ foo: 'baz' }, { agent }) + await agent.execute('doSomething', { foo: 'bar' }) + expect(doSomething).toBeCalledWith({ foo: 'bar' }, { agent }) + }) + + it('should expose only authorized methods', async () => { + const doSomething = jest.fn() + const baz = jest.fn() + const plugin: IAgentPlugin = { + methods: { + foo: jest.fn(), + bar: jest.fn(), + }, + } + const agent = new Agent({ + authorizedMethods: ['bar', 'baz'], + plugins: [plugin], + overrides: { + doSomething, + baz, + }, + }) + + //@ts-ignore + expect(agent.doSomething).toEqual(undefined) + expect(agent.execute('doSomething', { foo: 'bar' })).rejects.toEqual( + Error('Method not available: doSomething'), + ) + + //@ts-ignore + await agent.bar({ foo: 'baz' }) + expect(plugin.methods.bar).toBeCalledWith({ foo: 'baz' }, { agent }) + await agent.execute('baz', { foo: 'bar' }) + expect(baz).toBeCalledWith({ foo: 'bar' }, { agent }) + + expect(agent.availableMethods()).toEqual(['bar', 'baz']) + }) + + it('should pass through context', async () => { + const plugin: IAgentPlugin = { + methods: { + doSomething: jest.fn(), + }, + } + const agent = new Agent({ + context: { + authorizedDid: 'did:example:123', + }, + plugins: [plugin], + }) + + //@ts-ignore + await agent.doSomething({ foo: 'baz' }) + expect(plugin.methods.doSomething).toBeCalledWith( + { foo: 'baz' }, + { agent, authorizedDid: 'did:example:123' }, + ) + await agent.execute('doSomething', { foo: 'bar' }) + expect(plugin.methods.doSomething).toBeCalledWith( + { foo: 'bar' }, + { agent, authorizedDid: 'did:example:123' }, + ) + }) +}) diff --git a/packages/daf-core/src/__tests__/default.test.ts b/packages/daf-core/src/__tests__/default.test.ts deleted file mode 100644 index 448d23f49..000000000 --- a/packages/daf-core/src/__tests__/default.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -describe('daf-core', () => { - const a = 100 - it('should run a dummy test', () => { - expect(a).toEqual(100) - }) -}) diff --git a/packages/daf-core/src/agent.ts b/packages/daf-core/src/agent.ts index 8a3738e06..921452c7f 100644 --- a/packages/daf-core/src/agent.ts +++ b/packages/daf-core/src/agent.ts @@ -1,22 +1,49 @@ import { IAgentBase, TMethodMap, IAgentPlugin } from './types' import Debug from 'debug' +const filterUnauthorizedMethods = (methods: TMethodMap, authorizedMethods?: string[]): TMethodMap => { + // All methods are authorized by default + if (!authorizedMethods) { + return methods + } + + const result: TMethodMap = {} + for (const methodName of Object.keys(methods)) { + if (authorizedMethods.includes(methodName)) { + result[methodName] = methods[methodName] + } + } + + return result +} + export class Agent implements IAgentBase { readonly methods: TMethodMap = {} private context: Record private protectedMethods = ['execute', 'availableMethods'] - constructor(options?: { plugins?: IAgentPlugin[]; overrides?: TMethodMap; context?: Record }) { + constructor(options?: { + plugins?: IAgentPlugin[] + overrides?: TMethodMap + authorizedMethods?: string[] + context?: Record + }) { this.context = options?.context if (options?.plugins) { for (const plugin of options.plugins) { - this.methods = { ...this.methods, ...plugin.methods } + this.methods = { + ...this.methods, + ...filterUnauthorizedMethods(plugin.methods, options.authorizedMethods), + } } } if (options?.overrides) { - this.methods = { ...this.methods, ...options.overrides } + this.methods = { + ...this.methods, + ...filterUnauthorizedMethods(options.overrides, options.authorizedMethods), + } } for (const method of Object.keys(this.methods)) {