"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SecurityMetadataDecryptor = exports.SecurityMetadataType = void 0;
const utils_1 = require("../utils");
const models_1 = require("../../icc-api/model/models");
var AccessLevelEnum = models_1.SecureDelegation.AccessLevelEnum;
var SecurityMetadataType;
(function (SecurityMetadataType) {
    SecurityMetadataType[SecurityMetadataType["SecretId"] = 1] = "SecretId";
    SecurityMetadataType[SecurityMetadataType["EncryptionKey"] = 2] = "EncryptionKey";
    SecurityMetadataType[SecurityMetadataType["OwningEntityId"] = 3] = "OwningEntityId";
})(SecurityMetadataType || (exports.SecurityMetadataType = SecurityMetadataType = {}));
class SecurityMetadataDecryptor {
    constructor(exchangeKeysManager, primitives, exchangeData, exchangeDataMap, secureDelegationsEncryption, dataOwnerApi) {
        this.exchangeKeysManager = exchangeKeysManager;
        this.primitives = primitives;
        this.exchangeData = exchangeData;
        this.exchangeDataMap = exchangeDataMap;
        this.secureDelegationsEncryption = secureDelegationsEncryption;
        this.dataOwnerApi = dataOwnerApi;
    }
    decryptLegacyDelegations(entities, dataOwnersHierarchySubset, metadataType) {
        return __awaiter(this, void 0, void 0, function* () {
            // For legacy delegations there is no advantage in doing stuff in bulk.
            const res = {};
            for (const e of entities) {
                const allDecrypted = yield this.extractFromLegacyDelegations(e, dataOwnersHierarchySubset, metadataType);
                const deduplicatedDecrypted = new Set(allDecrypted.map((x) => x.decrypted));
                res[e.id] = [...deduplicatedDecrypted];
            }
            return res;
        });
    }
    extractFromLegacyDelegations(entity, dataOwnersHierarchySubset, metadataType) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b, _c;
            if (!dataOwnersHierarchySubset.length)
                throw new Error("`dataOwnersHierarchySubset` can't be empty");
            let delegations;
            let validateDecrypted = (x) => Promise.resolve(!!x);
            let mapDecrypted = (x) => x;
            switch (metadataType) {
                case SecurityMetadataType.SecretId:
                    delegations = (_a = entity.delegations) !== null && _a !== void 0 ? _a : {};
                    break;
                case SecurityMetadataType.EncryptionKey:
                    delegations = (_b = entity.encryptionKeys) !== null && _b !== void 0 ? _b : {};
                    validateDecrypted = (key) => __awaiter(this, void 0, void 0, function* () {
                        if (!/^[0-9A-Fa-f\-]+$/g.test(key))
                            return false;
                        try {
                            yield this.primitives.AES.importKey('raw', (0, utils_1.hex2ua)(key.replace(/-/g, '')));
                            return true;
                        }
                        catch (e) {
                            console.warn(`Could not import key ${key} as an encryption key.`, e);
                            return false;
                        }
                    });
                    mapDecrypted = (key) => key.replace(/-/g, '');
                    break;
                case SecurityMetadataType.OwningEntityId:
                    delegations = (_c = entity.cryptedForeignKeys) !== null && _c !== void 0 ? _c : {};
                    break;
                default:
                    throw new Error(`Internal error: invalid SecurityMetadataType ${metadataType}`);
            }
            const delegationsWithOwner = Object.entries(delegations).flatMap(([delegateId, delegations]) => dataOwnersHierarchySubset.some((dataOwnerId) => dataOwnerId === delegateId)
                ? this.populateLegacyDelegationDelegate(delegateId, delegations)
                : this.populateLegacyDelegationDelegate(delegateId, delegations.filter((d) => dataOwnersHierarchySubset.some((dataOwnerId) => d.owner === dataOwnerId))));
            const res = [];
            for (const delegation of delegationsWithOwner) {
                const decrypted = yield this.tryDecryptLegacyDelegation(delegation, (k) => validateDecrypted(k));
                if (decrypted)
                    res.push({
                        decrypted: mapDecrypted(decrypted),
                        dataOwnersWithAccess: delegation.owner ? [delegation.owner, delegation.delegatedTo] : [delegation.delegatedTo],
                    });
            }
            return res;
        });
    }
    populateLegacyDelegationDelegate(delegateId, delegations) {
        return delegations.map((d) => (d.delegatedTo === delegateId ? d : Object.assign(Object.assign({}, d), { delegatedTo: delegateId })));
    }
    tryDecryptLegacyDelegation(delegation, validateDecrypted) {
        return __awaiter(this, void 0, void 0, function* () {
            const exchangeKeys = yield this.exchangeKeysManager.getDecryptionExchangeKeysFor(delegation.owner, delegation.delegatedTo);
            for (const key of exchangeKeys) {
                try {
                    // Format of encrypted key for any delegation should be entityId:key, but with the merging of entities the entityId might not match the
                    // current id. As a checksum we are only verifying that the decrypted bytes can be represented as a string with exactly one ':'.
                    // Additionally, we also have a validator that is specific for each type of delegation content (encryption key, secret id, ...)
                    const decrypted = (0, utils_1.ua2string)(yield this.primitives.AES.decrypt(key, (0, utils_1.hex2ua)(delegation.key)));
                    const decryptedSplit = decrypted.split(':');
                    if (decryptedSplit.length === 2) {
                        if (yield validateDecrypted(decryptedSplit[1]))
                            return decryptedSplit[1];
                    }
                    else {
                        console.warn("Error in the decrypted delegation: content should contain exactly 1 ':', the delegation is ignored.");
                    }
                }
                catch (e) {
                    // Do nothing: the delegation uses another exchange key owner->delegator
                }
            }
        });
    }
    decryptSecureDelegationsUsingCache(entities, dataOwnersHierarchySubset, metadataType) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b, _c, _d;
            const toSearchById = new Set([]);
            const toSearchByDelegationKey = new Set([]);
            for (const e of entities) {
                for (const [delegationKey, delegation] of Object.entries((_b = (_a = e.securityMetadata) === null || _a === void 0 ? void 0 : _a.secureDelegations) !== null && _b !== void 0 ? _b : {})) {
                    if (!delegation.delegator ||
                        !delegation.delegate ||
                        dataOwnersHierarchySubset.some((it) => it == delegation.delegator || it == delegation.delegate)) {
                        if (delegation.exchangeDataId) {
                            toSearchById.add(delegation.exchangeDataId);
                        }
                        else {
                            toSearchByDelegationKey.add(delegationKey);
                        }
                    }
                }
            }
            const cachedByExchangeDataId = yield this.exchangeData.getDecryptionDataKeyByIds([...toSearchById], false);
            const cachedByDelegationKey = yield this.exchangeData.getCachedDecryptionDataKeyByAccessControlHash([...toSearchByDelegationKey]);
            const res = {};
            for (const entity of entities) {
                const currEntityRes = new Set([]);
                for (const [delegationKey, delegation] of Object.entries((_d = (_c = entity.securityMetadata) === null || _c === void 0 ? void 0 : _c.secureDelegations) !== null && _d !== void 0 ? _d : {})) {
                    const cached = delegation.exchangeDataId ? cachedByExchangeDataId[delegation.exchangeDataId] : cachedByDelegationKey[delegationKey];
                    if ((cached === null || cached === void 0 ? void 0 : cached.exchangeKey) &&
                        dataOwnersHierarchySubset.some((it) => { var _a, _b; return it == ((_a = cached === null || cached === void 0 ? void 0 : cached.exchangeData) === null || _a === void 0 ? void 0 : _a.delegator) || it == ((_b = cached === null || cached === void 0 ? void 0 : cached.exchangeData) === null || _b === void 0 ? void 0 : _b.delegate); })) {
                        ;
                        (yield this.decryptSecureDelegationMetadata(delegation, metadataType, cached.exchangeKey)).forEach((decrypted) => currEntityRes.add(decrypted));
                    }
                }
                res[entity.id] = [...currEntityRes];
            }
            return res;
        });
    }
    hasSecurityMetadataOfType(delegation, metadataType) {
        var _a, _b, _c;
        switch (metadataType) {
            case SecurityMetadataType.SecretId:
                return ((_a = delegation.secretIds) !== null && _a !== void 0 ? _a : []).length > 0;
            case SecurityMetadataType.EncryptionKey:
                return ((_b = delegation.encryptionKeys) !== null && _b !== void 0 ? _b : []).length > 0;
            case SecurityMetadataType.OwningEntityId:
                return ((_c = delegation.owningEntityIds) !== null && _c !== void 0 ? _c : []).length > 0;
            default:
                throw new Error('Internal error: invalid metadata type');
        }
    }
    decryptSecureDelegationsUsingKnownExchangeData(entities, dataOwnersHierarchySubset, metadataType) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b, _c, _d;
            const toSearchById = new Set([]);
            for (const e of entities) {
                for (const delegation of Object.values((_b = (_a = e.securityMetadata) === null || _a === void 0 ? void 0 : _a.secureDelegations) !== null && _b !== void 0 ? _b : {})) {
                    if (dataOwnersHierarchySubset.some((it) => it == delegation.delegator || it == delegation.delegate)) {
                        if (delegation.exchangeDataId && this.hasSecurityMetadataOfType(delegation, metadataType)) {
                            toSearchById.add(delegation.exchangeDataId);
                        }
                    }
                }
            }
            const retrievedByExchangeDataId = toSearchById.size > 0 ? yield this.exchangeData.getDecryptionDataKeyByIds([...toSearchById], true) : {};
            const res = {};
            for (const entity of entities) {
                const currEntityRes = new Set([]);
                for (const delegation of Object.values((_d = (_c = entity.securityMetadata) === null || _c === void 0 ? void 0 : _c.secureDelegations) !== null && _d !== void 0 ? _d : {})) {
                    const currDelegationExchangeData = delegation.exchangeDataId ? retrievedByExchangeDataId[delegation.exchangeDataId] : undefined;
                    if (currDelegationExchangeData === null || currDelegationExchangeData === void 0 ? void 0 : currDelegationExchangeData.exchangeKey) {
                        ;
                        (yield this.decryptSecureDelegationMetadata(delegation, metadataType, currDelegationExchangeData.exchangeKey)).forEach((decrypted) => currEntityRes.add(decrypted));
                    }
                }
                res[entity.id] = [...currEntityRes];
            }
            return res;
        });
    }
    decryptSecureDelegationsUsingExchangeDataMap(entities, dataOwnersHierarchySubset, metadataType) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b, _c, _d;
            const toSearchByExchangeDataMap = new Set([]);
            for (const e of entities) {
                for (const [delegationKey, delegation] of Object.entries((_b = (_a = e.securityMetadata) === null || _a === void 0 ? void 0 : _a.secureDelegations) !== null && _b !== void 0 ? _b : {})) {
                    if (dataOwnersHierarchySubset.some((it) => it == delegation.delegator || it == delegation.delegate)) {
                        if (!delegation.exchangeDataId && this.hasSecurityMetadataOfType(delegation, metadataType)) {
                            toSearchByExchangeDataMap.add(delegationKey);
                        }
                    }
                }
            }
            const exchangeDataMaps = toSearchByExchangeDataMap.size > 0 ? yield this.exchangeDataMap.getExchangeDataMapBatch([...toSearchByExchangeDataMap]) : [];
            const exchangeDataIdByDelegationKey = {};
            for (const exchangeDataMap of exchangeDataMaps) {
                const decrypted = yield this.secureDelegationsEncryption.decryptExchangeDataId(exchangeDataMap.encryptedExchangeDataIds);
                if (decrypted) {
                    exchangeDataIdByDelegationKey[exchangeDataMap.id] = decrypted;
                }
            }
            if (Object.keys(exchangeDataIdByDelegationKey).length == 0)
                return {};
            const retrievedByExchangeDataId = Object.values(exchangeDataIdByDelegationKey).length > 0
                ? yield this.exchangeData.getDecryptionDataKeyByIds([...new Set(Object.values(exchangeDataIdByDelegationKey))], true)
                : {};
            const res = {};
            for (const entity of entities) {
                const currEntityRes = new Set([]);
                for (const [delegationKey, delegation] of Object.entries((_d = (_c = entity.securityMetadata) === null || _c === void 0 ? void 0 : _c.secureDelegations) !== null && _d !== void 0 ? _d : {})) {
                    const exchangeDataId = exchangeDataIdByDelegationKey[delegationKey];
                    const cached = exchangeDataId ? retrievedByExchangeDataId[exchangeDataId] : undefined;
                    if (cached === null || cached === void 0 ? void 0 : cached.exchangeKey) {
                        ;
                        (yield this.decryptSecureDelegationMetadata(delegation, metadataType, cached.exchangeKey)).forEach((decrypted) => currEntityRes.add(decrypted));
                    }
                }
                res[entity.id] = [...currEntityRes];
            }
            return res;
        });
    }
    decryptAll(entity, dataOwnersHierarchySubset, metadataType) {
        return __awaiter(this, void 0, void 0, function* () {
            const allExtractedWithDataOwners = [
                ...(yield this.decryptAllLegacyDelegations(entity, dataOwnersHierarchySubset, metadataType)),
                ...(yield this.decryptAllSecureDelegations(entity, dataOwnersHierarchySubset, metadataType)),
            ];
            return dataOwnersHierarchySubset.map((dataOwner) => {
                const decryptedEntriesForDataOwner = allExtractedWithDataOwners
                    .filter((x) => x.dataOwnersWithAccess.some((d) => dataOwner == d))
                    .map((x) => x.decrypted);
                return {
                    ownerId: dataOwner,
                    extracted: [...new Set(decryptedEntriesForDataOwner)],
                };
            });
        });
    }
    decryptAllLegacyDelegations(entity, dataOwnersHierarchySubset, metadataType) {
        return __awaiter(this, void 0, void 0, function* () {
            return this.extractFromLegacyDelegations(entity, dataOwnersHierarchySubset, metadataType);
        });
    }
    decryptAllSecureDelegations(entity, dataOwnersHierarchySubset, metadataType) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b;
            const secureDelegationEntries = Object.entries((_b = (_a = entity.securityMetadata) === null || _a === void 0 ? void 0 : _a.secureDelegations) !== null && _b !== void 0 ? _b : {});
            const toSearchByDelegationKey = secureDelegationEntries.flatMap(([delegationKey, delegation]) => {
                if (!delegation.exchangeDataId) {
                    return [delegationKey];
                }
                else {
                    return [];
                }
            });
            const exchangeDataByDelegationKey = yield this.exchangeData.getCachedDecryptionDataKeyByAccessControlHash(toSearchByDelegationKey);
            const toSearchWithExchangeDataMap = secureDelegationEntries.flatMap(([delegationKey, delegation]) => {
                if (!delegation.exchangeDataId &&
                    dataOwnersHierarchySubset.some((it) => delegation.delegate == it || delegation.delegator == it) &&
                    !exchangeDataByDelegationKey[delegationKey] &&
                    this.hasSecurityMetadataOfType(delegation, metadataType)) {
                    return [delegationKey];
                }
                else {
                    return [];
                }
            });
            const exchangeDataMaps = toSearchWithExchangeDataMap.length > 0 ? yield this.exchangeDataMap.getExchangeDataMapBatch(toSearchWithExchangeDataMap) : [];
            const exchangeDataIdByDelegationKey = {};
            for (const exchangeDataMap of exchangeDataMaps) {
                const decrypted = yield this.secureDelegationsEncryption.decryptExchangeDataId(exchangeDataMap.encryptedExchangeDataIds);
                if (decrypted) {
                    exchangeDataIdByDelegationKey[exchangeDataMap.id] = decrypted;
                }
            }
            const toSearchDirectlyById = secureDelegationEntries.flatMap(([delegationKey, delegation]) => {
                if (delegation.exchangeDataId &&
                    dataOwnersHierarchySubset.some((it) => delegation.delegate == it || delegation.delegator == it) &&
                    !exchangeDataByDelegationKey[delegationKey] &&
                    this.hasSecurityMetadataOfType(delegation, metadataType)) {
                    return [delegation.exchangeDataId];
                }
                else {
                    return [];
                }
            });
            const allExchangeDataIdToRetrieve = [...new Set([...toSearchDirectlyById, ...Object.values(exchangeDataIdByDelegationKey)])];
            const exchangeDataById = allExchangeDataIdToRetrieve.length > 0 ? yield this.exchangeData.getDecryptionDataKeyByIds(allExchangeDataIdToRetrieve, true) : {};
            const allExtractedWithDataOwners = [];
            for (const [delegationKey, delegation] of secureDelegationEntries) {
                let exchangeData = undefined;
                if (exchangeDataByDelegationKey[delegationKey]) {
                    exchangeData = exchangeDataByDelegationKey[delegationKey];
                }
                else if (delegation.exchangeDataId) {
                    exchangeData = exchangeDataById[delegation.exchangeDataId];
                }
                else if (exchangeDataIdByDelegationKey[delegationKey]) {
                    exchangeData = exchangeDataById[exchangeDataIdByDelegationKey[delegationKey]];
                }
                if (exchangeData && exchangeData.exchangeKey) {
                    const decrypted = yield this.decryptSecureDelegationMetadata(delegation, metadataType, exchangeData.exchangeKey);
                    for (const d of decrypted) {
                        allExtractedWithDataOwners.push({
                            decrypted: d,
                            dataOwnersWithAccess: [exchangeData.exchangeData.delegator, exchangeData.exchangeData.delegate],
                        });
                    }
                }
            }
            return allExtractedWithDataOwners;
        });
    }
    getDelegationMemberDetails(typedEntity) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b;
            const res = {};
            // 1. Add all explicit data owners, keep only delegations with at least an anonymous data owner to check later
            let remainingDelegations = Object.entries((_b = (_a = typedEntity.entity.securityMetadata) === null || _a === void 0 ? void 0 : _a.secureDelegations) !== null && _b !== void 0 ? _b : {});
            let updatedRemainingDelegations = [];
            for (const delegationEntry of remainingDelegations) {
                const delegation = delegationEntry[1];
                if (delegation.delegator && delegation.delegate) {
                    res[delegationEntry[0]] = {
                        delegate: delegation.delegate,
                        delegator: delegation.delegator,
                        fullyExplicit: true,
                        accessLevel: delegation.permissions,
                        accessControlSecret: undefined,
                    };
                }
                else {
                    updatedRemainingDelegations.push(delegationEntry);
                }
            }
            remainingDelegations = updatedRemainingDelegations;
            if (!remainingDelegations.length)
                return res;
            updatedRemainingDelegations = [];
            // 2. Attempt to identify the anonymous data owner of remaining delegations by checking if we have the exchange data cached by hash
            // Note: we can find exchange data by hash only if we could successfully decrypt it
            const cachedExchangeData = yield this.exchangeData.getCachedDecryptionDataKeyByAccessControlHash(remainingDelegations.map((d) => d[0]));
            for (const delegationEntry of remainingDelegations) {
                const exchangeDataOfDelegation = cachedExchangeData[delegationEntry[0]];
                if (exchangeDataOfDelegation) {
                    res[delegationEntry[0]] = {
                        delegate: exchangeDataOfDelegation.exchangeData.delegate,
                        delegator: exchangeDataOfDelegation.exchangeData.delegator,
                        fullyExplicit: false,
                        accessLevel: delegationEntry[1].permissions,
                        accessControlSecret: exchangeDataOfDelegation.accessControlSecret,
                    };
                }
                else {
                    updatedRemainingDelegations.push(delegationEntry);
                }
            }
            remainingDelegations = updatedRemainingDelegations;
            if (!remainingDelegations.length)
                return res;
            updatedRemainingDelegations = [];
            // 3. Attempt to identify the anonymous data owner of remaining delegations between us (or one of our parents) and an anonymous data owner
            const hierarchy = yield this.dataOwnerApi.getCurrentDataOwnerHierarchyIds();
            const remainingExchangeDataMaps = yield this.exchangeDataMap.getExchangeDataMapBatch(remainingDelegations
                .filter(([_, delegation]) => hierarchy.some((x) => x == delegation.delegator || x == delegation.delegate))
                .map(([hash, _]) => hash));
            const exchangeDataIdByDelegationKey = {};
            for (const exchangeDataMap of remainingExchangeDataMaps) {
                if (!!exchangeDataMap.encryptedExchangeDataIds) {
                    const decrypted = yield this.secureDelegationsEncryption.decryptExchangeDataId(exchangeDataMap.encryptedExchangeDataIds);
                    if (decrypted) {
                        exchangeDataIdByDelegationKey[exchangeDataMap.id] = decrypted;
                    }
                }
            }
            const exchangeDataByIds = yield this.exchangeData.getDecryptionDataKeyByIds(Object.values(exchangeDataIdByDelegationKey), true);
            for (const [hash, delegation] of remainingDelegations) {
                if (hierarchy.some((x) => x === delegation.delegate || x === delegation.delegator)) {
                    const dataId = exchangeDataIdByDelegationKey[hash];
                    const exchangeDataInfo = dataId ? exchangeDataByIds[dataId] : undefined;
                    if (exchangeDataInfo) {
                        res[hash] = {
                            delegator: exchangeDataInfo.exchangeData.delegator,
                            delegate: exchangeDataInfo.exchangeData.delegate,
                            fullyExplicit: false,
                            accessLevel: delegation.permissions,
                            accessControlSecret: exchangeDataInfo.accessControlSecret,
                        };
                    }
                    else {
                        updatedRemainingDelegations.push([hash, delegation]);
                    }
                }
                else {
                    updatedRemainingDelegations.push([hash, delegation]);
                }
            }
            return Object.assign(Object.assign({}, res), Object.fromEntries(updatedRemainingDelegations.map(([hash, secureDelegation]) => [
                hash,
                {
                    delegator: secureDelegation.delegator,
                    delegate: secureDelegation.delegate,
                    fullyExplicit: false,
                    accessLevel: secureDelegation.permissions,
                    accessControlSecret: undefined,
                },
            ])));
        });
    }
    decryptSecureDelegationMetadata(delegation, metadataType, key) {
        return __awaiter(this, void 0, void 0, function* () {
            switch (metadataType) {
                case SecurityMetadataType.SecretId:
                    return this.secureDelegationsEncryption.decryptSecretIds(delegation, key);
                case SecurityMetadataType.EncryptionKey:
                    return this.secureDelegationsEncryption.decryptEncryptionKeys(delegation, key);
                case SecurityMetadataType.OwningEntityId:
                    return this.secureDelegationsEncryption.decryptOwningEntityIds(delegation, key);
                default:
                    throw new Error(`Internal error: invalid SecurityMetadataType ${metadataType}`);
            }
        });
    }
    getEntityLegacyDelegationAccessLevel(entity, dataOwnersHierarchySubset) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            if (!dataOwnersHierarchySubset.length)
                throw new Error("`dataOwnersHierarchySubset` can't be empty");
            // Legacy delegations provide write access
            if (Object.keys((_a = entity.delegations) !== null && _a !== void 0 ? _a : {}).some((delegate) => dataOwnersHierarchySubset.includes(delegate))) {
                return AccessLevelEnum.WRITE;
            }
            return undefined;
        });
    }
    getEntitySecureDelegationAccessLevel(entity, dataOwnersHierarchySubset) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b, _c, _d;
            if (!dataOwnersHierarchySubset.length)
                throw new Error("`dataOwnersHierarchySubset` can't be empty");
            // If the data owner is explicit all delegations he can access has his id. If the delegator is anonymous all delegations he can access are
            // accessible by hash. No mixed scenario possible.
            let accessibleDelegations = Object.values((_b = (_a = entity.securityMetadata) === null || _a === void 0 ? void 0 : _a.secureDelegations) !== null && _b !== void 0 ? _b : {}).filter((secureDelegation) => dataOwnersHierarchySubset.some((dataOwner) => dataOwner === secureDelegation.delegator || dataOwner === secureDelegation.delegate));
            if (!accessibleDelegations.length) {
                const availableCanonicalHashes = Object.keys(yield this.exchangeData.getCachedDecryptionDataKeyByAccessControlHash(Object.keys((_d = (_c = entity.securityMetadata) === null || _c === void 0 ? void 0 : _c.secureDelegations) !== null && _d !== void 0 ? _d : {})));
                accessibleDelegations = availableCanonicalHashes.map((hash) => { var _a, _b; return ((_b = (_a = entity.securityMetadata) === null || _a === void 0 ? void 0 : _a.secureDelegations) !== null && _b !== void 0 ? _b : {})[hash]; });
            }
            const permissions = accessibleDelegations.map((secureDelegation) => secureDelegation.permissions);
            let maxLevel = undefined;
            for (const permission of permissions) {
                if (permission === AccessLevelEnum.WRITE) {
                    return AccessLevelEnum.WRITE;
                }
                if (permission === AccessLevelEnum.READ) {
                    maxLevel = AccessLevelEnum.READ;
                }
            }
            return maxLevel;
        });
    }
    getEntityAccessLevel(entity, dataOwnersHierarchySubset) {
        return __awaiter(this, void 0, void 0, function* () {
            const legacyAccess = yield this.getEntityLegacyDelegationAccessLevel(entity, dataOwnersHierarchySubset);
            if (legacyAccess != undefined)
                return legacyAccess;
            return this.getEntitySecureDelegationAccessLevel(entity, dataOwnersHierarchySubset);
        });
    }
    hasAnyEncryptionKeys(entity) {
        var _a, _b, _c;
        return Object.keys((_a = entity.encryptionKeys) !== null && _a !== void 0 ? _a : {}).length > 0 || Object.keys((_c = (_b = entity.securityMetadata) === null || _b === void 0 ? void 0 : _b.secureDelegations) !== null && _c !== void 0 ? _c : {}).length > 0;
    }
}
exports.SecurityMetadataDecryptor = SecurityMetadataDecryptor;
//# sourceMappingURL=SecurityMetadataDecryptor.js.map