"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OwnerPermissionReader = void 0;
const policy_engine_1 = require("@solidlab/policy-engine");
const global_logger_factory_1 = require("global-logger-factory");
const IterableUtil_1 = require("../util/IterableUtil");
const IdentifierMap_1 = require("../util/map/IdentifierMap");
const MapUtil_1 = require("../util/map/MapUtil");
const PermissionReader_1 = require("./PermissionReader");
/**
 * Allows control access if the request is being made by an owner of the pod containing the resource.
 * This overrules any deductions made by the source reader:
 * if the target resource is owned by the client, they will always have control access.
 */
class OwnerPermissionReader extends PermissionReader_1.PermissionReader {
    logger = (0, global_logger_factory_1.getLoggerFor)(this);
    podStore;
    storageStrategy;
    reader;
    constructor(podStore, storageStrategy, reader) {
        super();
        this.podStore = podStore;
        this.storageStrategy = storageStrategy;
        this.reader = reader;
    }
    async canHandle(input) {
        return this.reader.canHandle(input);
    }
    async handle(input) {
        const requestedResources = input.requestedModes.distinctKeys();
        const auths = [...(0, IterableUtil_1.filter)(requestedResources, (id) => input.requestedModes.hasEntry(id, policy_engine_1.ACL.Control))];
        const owned = await this.findOwnedResources(auths, input.credentials.agent?.webId);
        const updatedRequest = new IdentifierMap_1.IdentifierSetMultiMap(input.requestedModes);
        for (const identifier of owned) {
            updatedRequest.deleteEntry(identifier, policy_engine_1.ACL.Control);
        }
        let result;
        if (updatedRequest.size > 0) {
            result = await this.reader.handle({ requestedModes: updatedRequest, credentials: input.credentials });
        }
        else {
            result = new IdentifierMap_1.IdentifierMap();
        }
        for (const identifier of owned) {
            const permissions = (0, MapUtil_1.getDefault)(result, identifier, () => ({}));
            permissions[policy_engine_1.ACL.Control] = true;
            result.set(identifier, permissions);
        }
        return result;
    }
    async findOwnedResources(identifiers, webId) {
        if (identifiers.length === 0) {
            this.logger.debug(`No resources found that need an ownership check.`);
            return [];
        }
        if (!webId) {
            this.logger.debug(`No WebId found for an ownership check on the pod.`);
            return [];
        }
        const pods = await this.findPods(identifiers);
        const owners = await this.findOwners(Object.values(pods));
        const result = [];
        for (const identifier of identifiers) {
            const webIds = owners[pods[identifier.path]];
            if (webIds?.includes(webId)) {
                result.push(identifier);
            }
        }
        return result;
    }
    /**
     * Finds all pods that contain the given identifiers.
     * Return value is a record where the keys are the identifiers and the values the associated pod.
     */
    async findPods(identifiers) {
        const pods = {};
        for (const identifier of identifiers) {
            let pod;
            try {
                pod = await this.storageStrategy.getStorageIdentifier(identifier);
            }
            catch {
                this.logger.error(`Unable to find root storage for ${identifier.path}`);
                continue;
            }
            pods[identifier.path] = pod.path;
        }
        return pods;
    }
    /**
     * Finds the owners of the given pods.
     * Return value is a record where the keys are the pods and the values are all the WebIDs that own this pod.
     */
    async findOwners(pods) {
        const owners = {};
        // Set to only have the unique values
        for (const baseUrl of new Set(pods)) {
            const pod = await this.podStore.findByBaseUrl(baseUrl);
            if (!pod) {
                this.logger.error(`Unable to find pod ${baseUrl}`);
                continue;
            }
            const podOwners = await this.podStore.getOwners(pod.id);
            if (!podOwners) {
                this.logger.error(`Unable to find owners for ${baseUrl}`);
                continue;
            }
            owners[baseUrl] = podOwners.map((owner) => owner.webId);
        }
        return owners;
    }
}
exports.OwnerPermissionReader = OwnerPermissionReader;
//# sourceMappingURL=OwnerPermissionReader.js.map