import { CredentialGroup } from '../../../src/authentication/Credentials'; import { AuxiliaryReader } from '../../../src/authorization/AuxiliaryReader'; import type { PermissionReaderInput, PermissionReader } from '../../../src/authorization/PermissionReader'; import type { AccessMap, PermissionMap, PermissionSet } from '../../../src/authorization/permissions/Permissions'; import { AccessMode } from '../../../src/authorization/permissions/Permissions'; import type { AuxiliaryStrategy } from '../../../src/http/auxiliary/AuxiliaryStrategy'; import type { ResourceIdentifier } from '../../../src/http/representation/ResourceIdentifier'; import { map } from '../../../src/util/IterableUtil'; import { IdentifierMap, IdentifierSetMultiMap } from '../../../src/util/map/IdentifierMap'; import { compareMaps } from '../../util/Util'; describe('An AuxiliaryReader', (): void => { const suffix1 = '.dummy1'; const suffix2 = '.dummy2'; const credentials = {}; const subjectIdentifier = { path: 'http://test.com/foo' }; const auxiliaryIdentifier1 = { path: 'http://test.com/foo.dummy1' }; const auxiliaryIdentifier2 = { path: 'http://test.com/foo.dummy2' }; const permissionSet: PermissionSet = { [CredentialGroup.agent]: { read: true }}; let source: jest.Mocked; let strategy: jest.Mocked; let reader: AuxiliaryReader; function handleSafe({ requestedModes }: PermissionReaderInput): PermissionMap { return new IdentifierMap(map(requestedModes.distinctKeys(), (identifier): [ResourceIdentifier, PermissionSet] => [ identifier, permissionSet ])); } beforeEach(async(): Promise => { source = { handleSafe: jest.fn(handleSafe), } as any; strategy = { isAuxiliaryIdentifier: jest.fn((identifier: ResourceIdentifier): boolean => identifier.path.endsWith(suffix1) || identifier.path.endsWith(suffix2)), getSubjectIdentifier: jest.fn((identifier: ResourceIdentifier): ResourceIdentifier => ({ path: identifier.path.slice(0, -suffix1.length) })), usesOwnAuthorization: jest.fn().mockReturnValue(false), } as any; reader = new AuxiliaryReader(source, strategy); }); it('handles resources by sending the updated parameters to the source.', async(): Promise => { const requestedModes: AccessMap = new IdentifierSetMultiMap([ [ auxiliaryIdentifier1, AccessMode.delete ], [{ path: 'http://example.com/other' }, AccessMode.read ], ]); const permissionMap: PermissionMap = new IdentifierMap([ [ subjectIdentifier, permissionSet ], [{ path: 'http://example.com/other' }, permissionSet ], [ auxiliaryIdentifier1, permissionSet ], ]); compareMaps(await reader.handle({ credentials, requestedModes }), permissionMap); const mock = source.handleSafe.mock.calls[0][0]; expect(mock.credentials).toBe(credentials); expect(mock.requestedModes.get(subjectIdentifier)).toEqual(new Set([ AccessMode.delete ])); expect(mock.requestedModes.get({ path: 'http://example.com/other' })).toEqual(new Set([ AccessMode.read ])); expect(mock.requestedModes.size).toBe(2); expect(mock.requestedModes).not.toEqual(requestedModes); }); it('applies an empty PermissionSet if no permissions were found for the subject.', async(): Promise => { source.handleSafe.mockResolvedValueOnce(new IdentifierMap()); const requestedModes: AccessMap = new IdentifierSetMultiMap([ [ auxiliaryIdentifier1, AccessMode.delete ], ]); const permissionMap: PermissionMap = new IdentifierMap([ [ auxiliaryIdentifier1, {}], ]); compareMaps(await reader.handle({ credentials, requestedModes }), permissionMap); }); it('combines modes if multiple different auxiliary resources have the same subject.', async(): Promise => { const requestedModes: AccessMap = new IdentifierSetMultiMap([ [ auxiliaryIdentifier1, AccessMode.write ], [ auxiliaryIdentifier2, AccessMode.read ], [ subjectIdentifier, AccessMode.delete ], ]); const resultSet = { [CredentialGroup.agent]: { read: true, write: true, delete: true }}; source.handleSafe.mockResolvedValueOnce(new IdentifierMap([[ subjectIdentifier, resultSet ]])); const permissionMap: PermissionMap = new IdentifierMap([ [ subjectIdentifier, resultSet ], [ auxiliaryIdentifier1, resultSet ], [ auxiliaryIdentifier2, resultSet ], ]); compareMaps(await reader.handle({ credentials, requestedModes }), permissionMap); expect(source.handleSafe.mock.calls[0][0].requestedModes.get(subjectIdentifier)) .toEqual(new Set([ AccessMode.write, AccessMode.read, AccessMode.delete ])); }); });