UNPKG

@bsv/wallet-toolbox

Version:

BRC100 conforming wallet, wallet storage and wallet signer components

227 lines 12.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const WalletPermissionsManager_fixtures_1 = require("./WalletPermissionsManager.fixtures"); const WalletPermissionsManager_1 = require("../WalletPermissionsManager"); jest.mock('@bsv/sdk', () => WalletPermissionsManager_fixtures_1.MockedBSV_SDK); describe('WalletPermissionsManager - Initialization & Configuration', () => { let underlying; beforeEach(() => { // Create a fresh underlying mock wallet before each test underlying = (0, WalletPermissionsManager_fixtures_1.mockUnderlyingWallet)(); }); afterEach(() => { jest.clearAllMocks(); }); it('should initialize with default config if none is provided', () => { const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'admin.domain.com'); // The manager internally defaults all config flags to true. const internalConfig = manager.config; expect(internalConfig.seekProtocolPermissionsForSigning).toBe(true); expect(internalConfig.seekProtocolPermissionsForEncrypting).toBe(true); expect(internalConfig.seekPermissionsForIdentityKeyRevelation).toBe(true); expect(internalConfig.encryptWalletMetadata).toBe(true); // The manager should store the admin originator const admin = manager.adminOriginator; expect(admin).toBe('admin.domain.com'); }); it('should initialize with partial config overrides, merging with defaults', () => { const partialConfig = { seekProtocolPermissionsForSigning: false, encryptWalletMetadata: false // The rest remain default = true }; const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'admin.domain.com', partialConfig); const internalConfig = manager.config; // Overridden to false expect(internalConfig.seekProtocolPermissionsForSigning).toBe(false); expect(internalConfig.encryptWalletMetadata).toBe(false); // Remaining defaults still true expect(internalConfig.seekBasketInsertionPermissions).toBe(true); expect(internalConfig.seekSpendingPermissions).toBe(true); }); it('should initialize with all config flags set to false', () => { const allFalse = { seekProtocolPermissionsForSigning: false, seekProtocolPermissionsForEncrypting: false, seekProtocolPermissionsForHMAC: false, seekPermissionsForKeyLinkageRevelation: false, seekPermissionsForPublicKeyRevelation: false, seekPermissionsForIdentityKeyRevelation: false, seekPermissionsForIdentityResolution: false, seekBasketInsertionPermissions: false, seekBasketRemovalPermissions: false, seekBasketListingPermissions: false, seekPermissionWhenApplyingActionLabels: false, seekPermissionWhenListingActionsByLabel: false, seekCertificateDisclosurePermissions: false, seekCertificateAcquisitionPermissions: false, seekCertificateRelinquishmentPermissions: false, seekCertificateListingPermissions: false, encryptWalletMetadata: false, seekSpendingPermissions: false, differentiatePrivilegedOperations: false }; const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'admin.domain.com', allFalse); const internalConfig = manager.config; for (const [k, v] of Object.entries(allFalse)) { expect(internalConfig[k]).toBe(v); } }); it('should consider calls from the adminOriginator as admin, bypassing checks', async () => { const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'admin.domain.com'); // If we call a method that normally triggers permission checks (like createAction with a basket), // but pass in originator="admin.domain.com", we expect NO permission prompt or error. // We'll do a minimal createAction call. const result = await manager.createAction({ description: 'Insertion to user basket', outputs: [ { lockingScript: 'abcd', satoshis: 1000, outputDescription: 'some out desc', basket: 'some-user-basket' } ] }, 'admin.domain.com'); // If the manager truly bypassed checks for the admin, it won't queue a request // nor throw an error. The call should just succeed. expect(result).toBeDefined(); // Confirm the underlying createAction was actually called expect(underlying.createAction).toHaveBeenCalledTimes(1); // activeRequests map should be empty const activeRequests = manager.activeRequests; expect(activeRequests.size).toBe(0); }); it('should skip protocol permission checks for signing if seekProtocolPermissionsForSigning=false', async () => { const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'admin.domain.com', { seekProtocolPermissionsForSigning: false }); // Non-admin origin attempts "createSignature" with a protocolID // Normally, if config was true, we'd expect a request for permission. // But here we expect it to skip and proceed. await expect(manager.createSignature({ protocolID: [1, 'some-protocol'], privileged: false, data: [0x01, 0x02], keyID: '1' }, 'app.nonadmin.com')).resolves.not.toThrow(); // underlying createSignature is invoked expect(underlying.createSignature).toHaveBeenCalledTimes(1); // The manager’s internal request queue should remain empty const activeRequests = manager.activeRequests; expect(activeRequests.size).toBe(0); }); it('should enforce protocol permission checks for signing if seekProtocolPermissionsForSigning=true', async () => { // By default, or explicitly set to true, the manager enforces permission checks const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'admin.domain.com', { seekProtocolPermissionsForSigning: true }); // Non-admin origin tries createSignature -> must prompt for protocol permission const createSigPromise = manager.createSignature({ protocolID: [1, 'test-protocol'], keyID: '1', data: [0x10, 0x20], privileged: false }, 'nonadmin.com'); // The manager triggers a request. Let's see if the request queue has an entry: const activeRequests = manager.activeRequests; // We may not see an entry synchronously because `ensureProtocolPermission()` is async, // but once the promise gets to that stage, it populates the queue. // Wait a short tick to let the async code run await new Promise(res => setTimeout(res, 10)); expect(activeRequests.size).toBeGreaterThan(0); // We'll forcibly deny the request so the test can conclude: const firstRequestKey = Array.from(activeRequests.keys())[0]; const firstRequestQueue = activeRequests.get(firstRequestKey); if (firstRequestQueue && firstRequestQueue.pending.length > 0) { manager.denyPermission(firstRequestKey); } // The promise eventually rejects with "Permission denied." await expect(createSigPromise).rejects.toThrow(/Permission denied/); }); it('should skip basket insertion permission checks if seekBasketInsertionPermissions=false', async () => { const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'admin.domain.com', { seekBasketInsertionPermissions: false }); // Spending authorization is still required, grant it. manager.bindCallback('onSpendingAuthorizationRequested', jest.fn(x => { manager.grantPermission({ requestID: x.requestID, ephemeral: true }); })); // Non-admin origin tries to createAction specifying a basket await expect(manager.createAction({ description: 'Insert to user basket', outputs: [ { lockingScript: '1234', satoshis: 888, basket: 'somebasket', outputDescription: 'some out desc' } ] }, 'some-user.com')).resolves.not.toThrow(); // Because insertion checks are disabled, no permission request should be queued const activeRequests = manager.activeRequests; expect(activeRequests.size).toBe(0); }); it('should skip all permission checks if all relevant config flags are false (except admin-only baskets, etc.)', async () => { const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'admin.domain.com', { // Only turning off relevant categories, i.e. we might set all false except we keep // differentiatePrivilegedOperations at default just to verify. Or set it to false as well. seekProtocolPermissionsForSigning: false, seekProtocolPermissionsForEncrypting: false, seekProtocolPermissionsForHMAC: false, seekPermissionsForKeyLinkageRevelation: false, seekPermissionsForPublicKeyRevelation: false, seekPermissionsForIdentityKeyRevelation: false, seekPermissionsForIdentityResolution: false, seekBasketInsertionPermissions: false, seekBasketRemovalPermissions: false, seekBasketListingPermissions: false, seekPermissionWhenApplyingActionLabels: false, seekPermissionWhenListingActionsByLabel: false, seekCertificateDisclosurePermissions: false, seekCertificateAcquisitionPermissions: false, seekCertificateRelinquishmentPermissions: false, seekCertificateListingPermissions: false, encryptWalletMetadata: false, seekSpendingPermissions: false, differentiatePrivilegedOperations: false }); // We'll do a few calls that would normally require checks: // 1) createSignature from non-admin await expect(manager.createSignature({ protocolID: [1, 'some-protocol'], data: [0x01], keyID: '1' }, 'nonadmin.com')).resolves.not.toThrow(); // 2) createAction to insert in a basket await expect(manager.createAction({ description: 'Inserting stuff', outputs: [ { lockingScript: '012345', satoshis: 1, basket: 'user-basket', outputDescription: 'some out desc' } ] }, 'nonadmin.com')).resolves.not.toThrow(); // 3) Acquire certificate await expect(manager.acquireCertificate({ type: 'base64-cert-type', certifier: '02abc...', acquisitionProtocol: 'direct', fields: { name: 'Bob' } }, 'nonadmin.com')).resolves.not.toThrow(); // Confirm no queued requests const activeRequests = manager.activeRequests; expect(activeRequests.size).toBe(0); }); it('should block usage of an admin-only protocol name if not called by admin', async () => { const manager = new WalletPermissionsManager_1.WalletPermissionsManager(underlying, 'admin.domain.com'); // A protocol name that starts with "admin" await expect(manager.createSignature({ protocolID: [1, 'admin super-secret-protocol'], data: [1, 2, 3], keyID: '1', privileged: false }, 'nonadmin.com')).rejects.toThrow(/admin-only/i); }); }); //# sourceMappingURL=WalletPermissionsManager.initialization.test.js.map