"use strict";
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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SecurityRuleRepository = void 0;
const common_1 = require("@nestjs/common");
const model_metadata_entity_1 = require("../entities/model-metadata.entity");
const role_metadata_entity_1 = require("../entities/role-metadata.entity");
const security_rule_entity_1 = require("../entities/security-rule.entity");
const solid_registry_1 = require("../helpers/solid-registry");
const crud_helper_service_1 = require("../services/crud-helper.service");
const typeorm_1 = require("typeorm");
const solid_base_repository_1 = require("./solid-base.repository");
let SecurityRuleRepository = class SecurityRuleRepository extends solid_base_repository_1.SolidBaseRepository {
    constructor(dataSource, solidRegistry, crudHelperService) {
        super(security_rule_entity_1.SecurityRule, dataSource, null, null);
        this.dataSource = dataSource;
        this.solidRegistry = solidRegistry;
        this.crudHelperService = crudHelperService;
    }
    async applySecurityRules(qb, modelSingularName, activeUser, securityRuleAlias = qb.alias) {
        const securityRules = this.solidRegistry.getSecurityRules(modelSingularName, activeUser.roles);
        if (!securityRules.length) {
            return qb;
        }
        const evaluatedRules = [];
        for (const rule of securityRules) {
            let evaluatedRule = null;
            try {
                if (rule.securityRuleConfigProvider) {
                    const securityRuleConfigProviderInstance = this.solidRegistry.getSecurityRuleConfigProviderInstance(rule.securityRuleConfigProvider);
                    if (!securityRuleConfigProviderInstance) {
                        throw new Error(`Unable to resolve instance for security rule config provider: ${rule.securityRuleConfigProvider}`);
                    }
                    evaluatedRule = await securityRuleConfigProviderInstance.securityRuleConfig(activeUser, rule);
                }
                else {
                    evaluatedRule = JSON.parse(this.resolveSecurityRuleConfig(rule.securityRuleConfig, activeUser));
                }
                evaluatedRules.push(evaluatedRule);
            }
            catch (error) {
                this.logger.error(`Error parsing security rule: ${rule.securityRuleConfig}`, error);
                this.logger.error(error.stack);
                throw error;
            }
        }
        qb.andWhere(new typeorm_1.Brackets(async (outerQb) => {
            for (const evaluatedRule of evaluatedRules) {
                if (evaluatedRule && evaluatedRule.filters) {
                    outerQb.orWhere(new typeorm_1.Brackets((innerQb) => {
                        this.crudHelperService.applyFilters(innerQb, evaluatedRule.filters, securityRuleAlias, qb);
                    }));
                }
            }
        }));
        return qb;
    }
    resolveSecurityRuleConfig(configString, activeUser) {
        return configString.replace('$activeUserId', activeUser.sub.toString());
    }
    async toDto(securityRule) {
        let populatedSecurityRule = securityRule;
        if (!securityRule.role || !securityRule.modelMetadata) {
            populatedSecurityRule = await this.findOne({
                where: {
                    id: securityRule.id,
                },
                relations: {
                    role: true,
                    modelMetadata: true,
                },
            });
        }
        return {
            id: populatedSecurityRule.id,
            name: populatedSecurityRule.name,
            description: populatedSecurityRule.description,
            roleId: populatedSecurityRule.role.id,
            roleUserKey: populatedSecurityRule.role.name,
            modelMetadataId: populatedSecurityRule.modelMetadata.id,
            modelMetadataUserKey: populatedSecurityRule.modelMetadata.singularName,
            securityRuleConfig: populatedSecurityRule.securityRuleConfig,
            securityRuleConfigProvider: populatedSecurityRule.securityRuleConfigProvider,
        };
    }
    async upsertWithDto(createDto) {
        const roleRepository = this.dataSource.getRepository(role_metadata_entity_1.RoleMetadata);
        if (createDto.roleId) {
            const role = await roleRepository.findOne({
                where: {
                    id: createDto.roleId,
                },
            });
            createDto['role'] = role;
        }
        if (createDto.roleUserKey) {
            const role = await roleRepository.findOne({
                where: {
                    name: createDto.roleUserKey,
                },
            });
            createDto['role'] = role;
        }
        const modelMetadataRepository = this.dataSource.getRepository(model_metadata_entity_1.ModelMetadata);
        if (createDto.modelMetadataId) {
            const modelMetadata = await modelMetadataRepository.findOne({
                where: {
                    id: createDto.modelMetadataId,
                },
            });
            createDto['modelMetadata'] = modelMetadata;
        }
        if (createDto.modelMetadataUserKey) {
            const modelMetadata = await modelMetadataRepository.findOne({
                where: {
                    singularName: createDto.modelMetadataUserKey,
                },
            });
            createDto['modelMetadata'] = modelMetadata;
        }
        const existingSecurityRule = await this.findOne({
            where: {
                name: createDto.name,
            },
        });
        if (existingSecurityRule) {
            const updatedSecurityRule = this.merge(existingSecurityRule, createDto);
            return this.save(updatedSecurityRule);
        }
        else {
            const securityRule = this.create(createDto);
            return this.save(securityRule);
        }
    }
};
exports.SecurityRuleRepository = SecurityRuleRepository;
exports.SecurityRuleRepository = SecurityRuleRepository = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [typeorm_1.DataSource,
        solid_registry_1.SolidRegistry,
        crud_helper_service_1.CrudHelperService])
], SecurityRuleRepository);
//# sourceMappingURL=security-rule.repository.js.map