import type { AuxiliaryStrategy } from '../http/auxiliary/AuxiliaryStrategy'; import type { ResourceIdentifier } from '../http/representation/ResourceIdentifier'; import { getLoggerFor } from '../logging/LogUtil'; import { IdentifierMap, IdentifierSetMultiMap } from '../util/map/IdentifierMap'; import type { MapEntry } from '../util/map/MapUtil'; import { modify } from '../util/map/MapUtil'; import type { PermissionReaderInput } from './PermissionReader'; import { PermissionReader } from './PermissionReader'; import type { AccessMap, AccessMode, PermissionMap } from './permissions/Permissions'; /** * Determines the permissions of auxiliary resources by finding those of the corresponding subject resources. */ export class AuxiliaryReader extends PermissionReader { protected readonly logger = getLoggerFor(this); private readonly reader: PermissionReader; private readonly auxiliaryStrategy: AuxiliaryStrategy; public constructor(reader: PermissionReader, auxiliaryStrategy: AuxiliaryStrategy) { super(); this.reader = reader; this.auxiliaryStrategy = auxiliaryStrategy; } public async handle({ requestedModes, credentials }: PermissionReaderInput): Promise { // Finds all the dependent auxiliary identifiers const auxiliaries = this.findAuxiliaries(requestedModes); // Replaces the dependent auxiliary identifies with the corresponding subject identifiers const updatedMap = modify(new IdentifierSetMultiMap(requestedModes), { add: auxiliaries.values(), remove: auxiliaries.keys() }); const result = await this.reader.handleSafe({ requestedModes: updatedMap, credentials }); // Extracts the auxiliary permissions based on the subject permissions for (const [ identifier, [ subject ]] of auxiliaries) { this.logger.debug(`Mapping ${subject.path} permissions to ${identifier.path}`); result.set(identifier, result.get(subject) ?? {}); } return result; } /** * Maps auxiliary resources that do not have their own authorization checks to their subject resource. */ private findAuxiliaries(requestedModes: AccessMap): IdentifierMap> { const auxiliaries = new IdentifierMap<[ResourceIdentifier, ReadonlySet]>(); for (const [ identifier, modes ] of requestedModes.entrySets()) { if (this.isDependentAuxiliary(identifier)) { auxiliaries.set(identifier, [ this.auxiliaryStrategy.getSubjectIdentifier(identifier), modes ]); } } return auxiliaries; } /** * Checks if the identifier is an auxiliary resource that uses subject permissions. */ private isDependentAuxiliary(identifier: ResourceIdentifier): boolean { return this.auxiliaryStrategy.isAuxiliaryIdentifier(identifier) && !this.auxiliaryStrategy.usesOwnAuthorization(identifier); } }