64 lines
2.8 KiB
TypeScript

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<PermissionMap> {
// 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<MapEntry<AccessMap>> {
const auxiliaries = new IdentifierMap<[ResourceIdentifier, ReadonlySet<AccessMode>]>();
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);
}
}