"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WacAllowHttpHandler = void 0;
const policy_engine_1 = require("@solidlab/policy-engine");
const global_logger_factory_1 = require("global-logger-factory");
const NotModifiedHttpError_1 = require("../util/errors/NotModifiedHttpError");
const Vocabularies_1 = require("../util/Vocabularies");
const OperationHttpHandler_1 = require("./OperationHttpHandler");
const VALID_METHODS = new Set(['HEAD', 'GET']);
/**
 * Adds all the available permissions to the response metadata,
 * which can be used to generate the correct WAC-Allow header.
 *
 * This class does many things similar to the {@link AuthorizingHttpHandler},
 * so in general it is a good idea to make sure all these classes cache their results.
 */
class WacAllowHttpHandler extends OperationHttpHandler_1.OperationHttpHandler {
    logger = (0, global_logger_factory_1.getLoggerFor)(this);
    credentialsExtractor;
    modesExtractor;
    permissionReader;
    operationHandler;
    constructor(args) {
        super();
        this.credentialsExtractor = args.credentialsExtractor;
        this.modesExtractor = args.modesExtractor;
        this.permissionReader = args.permissionReader;
        this.operationHandler = args.operationHandler;
    }
    async handle(input) {
        const { request, operation } = input;
        let response;
        try {
            response = await this.operationHandler.handleSafe(input);
        }
        catch (error) {
            // WAC-Allow headers need to be added to 304 responses
            // as the value can differ even if the representation is the same.
            if (NotModifiedHttpError_1.NotModifiedHttpError.isInstance(error)) {
                response = error;
            }
            else {
                throw error;
            }
        }
        const { metadata } = response;
        // WAC-Allow is only needed for HEAD/GET requests
        if (!VALID_METHODS.has(operation.method) || !metadata) {
            return response;
        }
        this.logger.debug('Determining available permissions.');
        const credentials = await this.credentialsExtractor.handleSafe(request);
        const requestedModes = await this.modesExtractor.handleSafe(operation);
        const availablePermissions = await this.permissionReader.handleSafe({ credentials, requestedModes });
        const permissionSet = availablePermissions.get(operation.target);
        if (permissionSet) {
            const user = permissionSet;
            let everyone;
            if (credentials.agent?.webId) {
                // Need to determine public permissions
                this.logger.debug('Determining public permissions');
                // Note that this call can potentially create a new lock on a resource that is already locked,
                // so a locker that allows multiple read locks on the same resource is required.
                const permissionMap = await this.permissionReader.handleSafe({ credentials: {}, requestedModes });
                everyone = permissionMap.get(operation.target) ?? {};
            }
            else {
                // User is not authenticated so public permissions are the same as agent permissions
                this.logger.debug('User is not authenticated so has public permissions');
                everyone = user;
            }
            this.logger.debug('Adding WAC-Allow metadata');
            this.addWacAllowMetadata(metadata, everyone, user);
        }
        if (NotModifiedHttpError_1.NotModifiedHttpError.isInstance(response)) {
            throw response;
        }
        return response;
    }
    /**
     * Converts the found permissions to triples and puts them in the metadata.
     */
    addWacAllowMetadata(metadata, everyone, user) {
        const modes = new Set([...Object.keys(user), ...Object.keys(everyone)]);
        for (const mode of modes) {
            const aclMode = this.toAclMode(mode);
            if (aclMode) {
                if (everyone[mode]) {
                    metadata.add(Vocabularies_1.AUTH.terms.publicMode, aclMode);
                }
                if (user[mode]) {
                    metadata.add(Vocabularies_1.AUTH.terms.userMode, aclMode);
                }
            }
        }
    }
    toAclMode(mode) {
        switch (mode) {
            case policy_engine_1.PERMISSIONS.Read: return policy_engine_1.ACL.terms.Read;
            case policy_engine_1.PERMISSIONS.Append: return policy_engine_1.ACL.terms.Append;
            case policy_engine_1.PERMISSIONS.Modify: return policy_engine_1.ACL.terms.Write;
            case policy_engine_1.ACL.Control: return policy_engine_1.ACL.terms.Control;
            default:
        }
    }
}
exports.WacAllowHttpHandler = WacAllowHttpHandler;
//# sourceMappingURL=WacAllowHttpHandler.js.map