var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import DatabaseRequestType from "../../BaseDatabase/DatabaseRequestType";
import TablePermission from "./TablePermission";
import ArrayUtil from "../../../../Utils/Array";
import DatabaseCommonInteractionPropsUtil, { PermissionType, } from "../../../../Types/BaseDatabase/DatabaseCommonInteractionPropsUtil";
import BadDataException from "../../../../Types/Exception/BadDataException";
import NotAuthorizedException from "../../../../Types/Exception/NotAuthorizedException";
import { PermissionHelper, } from "../../../../Types/Permission";
import { combineWithPrivacyClause } from "../../../Utils/PrivacyFilterUtil";
import QueryHelper from "../QueryHelper";
import QueryUtil from "../QueryUtil";
import CaptureSpan from "../../../Utils/Telemetry/CaptureSpan";
export default class AccessControlPermission {
    static async checkAccessControlBlockPermissionByModel(data) {
        const { modelType, props, type } = data;
        if (props.isRoot || props.isMasterAdmin) {
            return;
        }
        TablePermission.checkTableLevelBlockPermissions(modelType, props, type);
        const blockPermissionWithLabels = DatabaseCommonInteractionPropsUtil.getUserPermissions(props, PermissionType.Block).filter((permission) => {
            return permission.labelIds && permission.labelIds.length > 0;
        });
        if (blockPermissionWithLabels.length === 0) {
            return;
        }
        const modelPermissions = TablePermission.getTablePermission(modelType, type);
        const blockPermissionsBelongToThisModel = blockPermissionWithLabels.filter((blockPermission) => {
            let isModelPermission = false;
            for (const permission of modelPermissions) {
                if (permission.toString() === blockPermission.permission.toString()) {
                    isModelPermission = true;
                    break;
                }
            }
            return isModelPermission;
        });
        if (blockPermissionsBelongToThisModel.length === 0) {
            return;
        }
        // now check if the user has any of these labels in the block list, for this we need to fetch the model first.
        const fetchedModel = await data.fetchModelWithAccessControlIds();
        if (!fetchedModel) {
            throw new BadDataException(`${modelType.name} not found.`);
        }
        for (const blockPermissionBelongToThisModel of blockPermissionsBelongToThisModel) {
            const blockPermissionLabelIds = blockPermissionBelongToThisModel.labelIds || [];
            const blockPermissionLabelIdAsString = blockPermissionLabelIds.map((id) => {
                return id.toString();
            });
            if (blockPermissionLabelIds.length === 0) {
                continue;
            }
            const model = fetchedModel;
            const modelAccessControlColumnName = model.getAccessControlColumn();
            if (modelAccessControlColumnName) {
                const modelAccessControl = model.getColumnValue(modelAccessControlColumnName) || [];
                for (const accessControl of modelAccessControl) {
                    if (!accessControl.id) {
                        continue;
                    }
                    if (blockPermissionLabelIdAsString.includes(accessControl.id.toString())) {
                        throw new NotAuthorizedException(`You are not authorized to ${type.toLowerCase()} this ${model.singularName} because ${blockPermissionBelongToThisModel.permission} is in your team's permission block list.`);
                    }
                }
            }
        }
    }
    static async checkAccessControlPermissionByModel(data) {
        const { modelType, props, type } = data;
        if (props.isRoot || props.isMasterAdmin) {
            return;
        }
        // Check if the user has permission to delete or update the object in this table.
        TablePermission.checkTableLevelPermissions(modelType, props, type);
        // if the control is here, then the user has table level permissions.
        const model = new modelType();
        const modelAccessControlColumnName = model.getAccessControlColumn();
        if (modelAccessControlColumnName) {
            const accessControlIdsWhichUserHasAccessTo = this.getAccessControlIdsForModel(modelType, props, type);
            if (accessControlIdsWhichUserHasAccessTo.length === 0) {
                return; // The user has access to all resources, if no labels are specified.
            }
            const fetchedModel = await data.fetchModelWithAccessControlIds();
            if (!fetchedModel) {
                throw new BadDataException(`${model.singularName} not found.`);
            }
            const accessControlIdsWhichUserHasAccessToAsStrings = accessControlIdsWhichUserHasAccessTo.map((id) => {
                return id.toString();
            }) || [];
            // Check if the object has any of these access control ids.  if not, then throw an error.
            const modelAccessControl = fetchedModel.getColumnValue(modelAccessControlColumnName) || [];
            const modelAccessControlNames = [];
            for (const accessControl of modelAccessControl) {
                if (!accessControl.id) {
                    continue;
                }
                if (accessControlIdsWhichUserHasAccessToAsStrings.includes(accessControl.id.toString())) {
                    return;
                }
                const accessControlName = accessControl.getColumnValue("name");
                if (accessControlName) {
                    modelAccessControlNames.push(accessControlName);
                }
            }
            let errorString = `You do not have permission to ${type.toLowerCase()} this ${model.singularName}.`;
            if (modelAccessControlNames.length > 0) {
                errorString += ` You need to have one of the following labels: ${modelAccessControlNames.join(", ")}.`;
            }
            else {
                errorString = ` You do not have permission to ${type.toLowerCase()} ${model.singularName} without any labels.`;
            }
            throw new NotAuthorizedException(errorString);
        }
    }
    static async addAccessControlIdsToQuery(modelType, query, select, props, type) {
        const model = new modelType();
        // if the model has access control column, then add the access control labels to the query.
        if (model.getAccessControlColumn()) {
            const accessControlIds = this.getAccessControlIdsForQuery(modelType, query, select, props, type);
            if (accessControlIds.length > 0) {
                const accessControlColumn = model.getAccessControlColumn();
                const existingFilter = query[accessControlColumn];
                if (existingFilter === undefined || existingFilter === null) {
                    query[accessControlColumn] = accessControlIds;
                }
                else {
                    /*
                     * The caller already filters on the access-control column (e.g.
                     * "show monitors with label X"). Overwriting that filter with the
                     * permitted set would silently widen it to "any permitted label".
                     * Both predicates must hold independently — a record with labels
                     * {X, Y} (X requested, Y permitted) must match even when X itself
                     * is not permitted — so keep the caller's filter on the relation
                     * key and AND the permitted-set predicate onto _id as a join-table
                     * subquery.
                     */
                    const manyToManyMeta = QueryUtil.getManyToManyRelationMetadata(modelType, accessControlColumn);
                    if (manyToManyMeta) {
                        query._id = combineWithPrivacyClause(query._id, QueryHelper.anyOfEntitiesInManyToMany({
                            values: accessControlIds,
                            joinTableName: manyToManyMeta.joinTableName,
                            ownerColumnName: manyToManyMeta.ownerColumnName,
                            relationColumnName: manyToManyMeta.relationColumnName,
                        }));
                    }
                    else {
                        /*
                         * Join metadata unavailable — fail closed to the permitted set.
                         * The caller's filter is dropped, but access never widens.
                         */
                        query[accessControlColumn] = accessControlIds;
                    }
                }
            }
        }
        return query;
    }
    static getAccessControlIdsForModel(modelType, props, type) {
        let labelIds = [];
        // check model level permissions.
        const modelLevelPermissions = TablePermission.getTablePermission(modelType, type);
        const modelLevelLabelIds = this.getAccessControlIdsByPermissions(modelLevelPermissions, props);
        labelIds = [...labelIds, ...modelLevelLabelIds];
        // get distinct labelIds
        const distinctLabelIds = ArrayUtil.removeDuplicatesFromObjectIDArray(labelIds);
        return distinctLabelIds;
    }
    static getAccessControlIdsForQuery(modelType, query, select, props, type) {
        const model = new modelType();
        let labelIds = [];
        let columnsToCheckPermissionFor = Object.keys(query);
        if (select) {
            columnsToCheckPermissionFor = [
                ...columnsToCheckPermissionFor,
                ...Object.keys(select),
            ];
        }
        labelIds = this.getAccessControlIdsForModel(modelType, props, type);
        for (const column of columnsToCheckPermissionFor) {
            const accessControl = model.getColumnAccessControlFor(column);
            if (!accessControl) {
                continue;
            }
            if (type === DatabaseRequestType.Read && accessControl.read) {
                const columnReadLabelIds = this.getAccessControlIdsByPermissions(accessControl.read, props);
                labelIds = [...labelIds, ...columnReadLabelIds];
            }
            if (type === DatabaseRequestType.Create && accessControl.create) {
                const columnCreateLabelIds = this.getAccessControlIdsByPermissions(accessControl.create, props);
                labelIds = [...labelIds, ...columnCreateLabelIds];
            }
            if (type === DatabaseRequestType.Update && accessControl.update) {
                const columnUpdateLabelIds = this.getAccessControlIdsByPermissions(accessControl.update, props);
                labelIds = [...labelIds, ...columnUpdateLabelIds];
            }
        }
        // get distinct labelIds
        const distinctLabelIds = ArrayUtil.removeDuplicatesFromObjectIDArray(labelIds);
        return distinctLabelIds;
    }
    static getAccessControlIdsByPermissions(permissions, props) {
        const userPermissions = DatabaseCommonInteractionPropsUtil.getUserPermissions(props, PermissionType.Allow);
        const nonAccessControlPermissionPermission = PermissionHelper.getNonAccessControlPermissions(userPermissions);
        const accessControlPermissions = PermissionHelper.getAccessControlPermissions(userPermissions);
        let labelIds = [];
        if (PermissionHelper.doesPermissionsIntersect(permissions, nonAccessControlPermissionPermission)) {
            return []; // if this is intersecting, then return empty array. We dont need to check for access control.
        }
        for (const permission of permissions) {
            for (const accessControlPermission of accessControlPermissions) {
                if (accessControlPermission.permission === permission &&
                    accessControlPermission.labelIds.length > 0) {
                    labelIds = [...labelIds, ...accessControlPermission.labelIds];
                }
            }
        }
        // remove duplicates
        labelIds = ArrayUtil.removeDuplicatesFromObjectIDArray(labelIds);
        return labelIds;
    }
}
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], AccessControlPermission, "checkAccessControlBlockPermissionByModel", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], AccessControlPermission, "checkAccessControlPermissionByModel", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object, Object, Object, String]),
    __metadata("design:returntype", Promise)
], AccessControlPermission, "addAccessControlIdsToQuery", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object, String]),
    __metadata("design:returntype", Array)
], AccessControlPermission, "getAccessControlIdsForModel", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object, Object, Object, String]),
    __metadata("design:returntype", Array)
], AccessControlPermission, "getAccessControlIdsForQuery", null);
//# sourceMappingURL=AccessControlPermission.js.map