"use strict";
/*
 * Copyright 2020 NEM
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.AgentCertificateService = void 0;
const fs_1 = require("fs");
const path_1 = require("path");
const logger_1 = require("../logger");
const LoggerFactory_1 = require("../logger/LoggerFactory");
const BootstrapUtils_1 = require("./BootstrapUtils");
const CertificateService_1 = require("./CertificateService");
const CommandUtils_1 = require("./CommandUtils");
const ConfigService_1 = require("./ConfigService");
const logger = LoggerFactory_1.default.getLogger(logger_1.LogType.System);
class AgentCertificateService {
    constructor(root, params) {
        this.root = root;
        this.params = params;
    }
    async run(networkType, symbolServerToolsImage, name, providedCertificates, customCertFolder) {
        const copyFrom = `${this.root}/config/agent-cert`;
        const certFolder = customCertFolder || BootstrapUtils_1.BootstrapUtils.getTargetNodesFolder(this.params.target, false, name, 'agent');
        await BootstrapUtils_1.BootstrapUtils.mkdir(certFolder);
        const metadataFile = path_1.join(certFolder, 'metadata.yml');
        if (!(await this.shouldGenerateCertificate(metadataFile, providedCertificates))) {
            logger.info(`Agent Certificates for node ${name} have been previously generated. Reusing...`);
            return;
        }
        await BootstrapUtils_1.BootstrapUtils.deleteFolder(certFolder);
        await BootstrapUtils_1.BootstrapUtils.mkdir(certFolder);
        const newCertsFolder = path_1.join(certFolder, 'new_certs');
        await BootstrapUtils_1.BootstrapUtils.mkdir(newCertsFolder);
        const generatedContext = { name };
        await BootstrapUtils_1.BootstrapUtils.generateConfiguration(generatedContext, copyFrom, certFolder, []);
        const agentPrivateKey = await CommandUtils_1.CommandUtils.resolvePrivateKey(networkType, providedCertificates.agent, ConfigService_1.KeyName.Agent, name, 'generating the Agent certificates');
        BootstrapUtils_1.BootstrapUtils.createDerFile(agentPrivateKey, path_1.join(certFolder, 'agent-ca.der'));
        const command = this.createCertCommands('/data');
        await BootstrapUtils_1.BootstrapUtils.writeTextFile(path_1.join(certFolder, 'createAgentCertificate.sh'), command);
        const cmd = ['bash', 'createAgentCertificate.sh'];
        const binds = [`${path_1.resolve(certFolder)}:/data:rw`];
        const userId = await BootstrapUtils_1.BootstrapUtils.resolveDockerUserFromParam(this.params.user);
        const { stdout, stderr } = await BootstrapUtils_1.BootstrapUtils.runImageUsingExec({
            image: symbolServerToolsImage,
            userId: userId,
            workdir: '/data',
            cmds: cmd,
            binds: binds,
        });
        if (stdout.indexOf('Certificate Created') < 0) {
            logger.info(BootstrapUtils_1.BootstrapUtils.secureString(stdout));
            logger.error(BootstrapUtils_1.BootstrapUtils.secureString(stderr));
            throw new Error('Certificate creation failed. Check the logs!');
        }
        const certificates = CertificateService_1.CertificateService.getCertificates(stdout);
        if (certificates.length != 1) {
            throw new Error('Certificate creation failed. 1 certificates should have been created but got: ' + certificates.length);
        }
        const agentCertificate = certificates[0];
        BootstrapUtils_1.BootstrapUtils.validateIsTrue(agentCertificate.privateKey === agentPrivateKey, 'Invalid agent private key');
        BootstrapUtils_1.BootstrapUtils.validateIsTrue(agentCertificate.publicKey === providedCertificates.agent.publicKey, 'Invalid agent public key');
        logger.info(`Agent Certificate for node ${name} created`);
        const metadata = {
            version: AgentCertificateService.METADATA_VERSION,
            agentPublicKey: providedCertificates.agent.publicKey,
        };
        await BootstrapUtils_1.BootstrapUtils.writeYaml(metadataFile, metadata, undefined);
    }
    async shouldGenerateCertificate(metadataFile, providedCertificates) {
        if (!fs_1.existsSync(metadataFile)) {
            return true;
        }
        try {
            const metadata = BootstrapUtils_1.BootstrapUtils.loadYaml(metadataFile, false);
            return (metadata.agentPublicKey !== providedCertificates.agent.publicKey ||
                metadata.version !== AgentCertificateService.METADATA_VERSION);
        }
        catch (e) {
            logger.warn(`Cannot load agent certificate metadata from file ${metadataFile}. Error: ${e.message}`, e);
            return true;
        }
    }
    // Alternative reading the public key from agent-ca.pubkey.pem
    // It's ok but it requires sshpk and unsure how to force an regeneration
    // private async shouldGenerateCertificate(certFolder: string, providedCertificates: AgentCertificates): Promise<boolean> {
    //   const pemFile = join(certFolder, 'agent-ca.pubkey.pem');
    //   if (!existsSync(pemFile)) {
    //     return true;
    //   }
    //   try {
    //     const data = readFileSync(pemFile);
    //     const key = sshpk.parseKey(data, 'pem');
    //     const storedPublicKey = Convert.uint8ToHex((key.part as any).A.data);
    //     return storedPublicKey !== providedCertificates.agent.publicKey;
    //   } catch (e) {
    //     logger.warn(`Cannot extract public key from file ${pemFile}. Error: ${e.message}`, e);
    //     return true;
    //   }
    // }
    createCertCommands(target) {
        return `set -e
cd ${target}

# prepare dirs/files
chmod 700 new_certs
touch index.txt

# Creating a self signed certificate for the agent. This will be created by bootstrap when setting up a supernode

cat agent-ca.der | openssl pkey -inform DER -outform PEM -out agent-ca.key.pem
openssl pkey -inform pem -in agent-ca.key.pem -text -noout
openssl pkey -in agent-ca.key.pem -pubout -out agent-ca.pubkey.pem

# create agent CA CSR
openssl req -config agent-ca.cnf -key agent-ca.key.pem -new -out agent-ca.csr.pem

rm createAgentCertificate.sh
rm agent-ca.der

echo "Certificate Created"
`;
    }
}
exports.AgentCertificateService = AgentCertificateService;
AgentCertificateService.METADATA_VERSION = 1;
