"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AzureKeyVaultSecretProvider = void 0;
/* eslint-disable max-len */
const logger_1 = require("../../../logger");
const node_cache_1 = require("../../node-cache");
const identity_1 = require("@azure/identity");
const keyvault_secrets_1 = require("@azure/keyvault-secrets");
const lodash_1 = __importDefault(require("lodash"));
class AzureKeyVaultSecretProvider {
    constructor(azureConfig, logger = logger_1.logger) {
        this.spydraCache = node_cache_1.NodeCache.getInstance();
        this.logger = logger_1.logger;
        this.getVaultName = (vaultName, regionName) => {
            let tempVaultName = vaultName;
            if (regionName) {
                tempVaultName = vaultName?.replace('{region}', regionName.replace(/\s+/g, '').toLowerCase());
            }
            return tempVaultName;
        };
        // https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/identity/identity/samples/AzureIdentityExamples.md#chaining-credentials
        /**
         * Fetches the secret automatically based on the configured provider.
         * If the provider is 'aws', it retrieves the secret from AWS Secret Manager.
         * If the provider is 'azure', it retrieves the secret from Azure Key Vault.
         * Throws an error if the required parameters are not provided.
         */
        this.autoFetchSecret = async () => {
            throw new Error('Method not implemented.');
        };
        this.normalizeKey = (key) => key.replace(/[^a-zA-Z0-9]/g, '-');
        this.get = async (secretName, secretKey) => {
            const cachedSecrets = this.spydraCache.get(`${this.vaultName}-${this.normalizeKey(secretName)}`) || {};
            if (lodash_1.default.isEmpty(cachedSecrets) === false
                && typeof cachedSecrets[secretKey] !== 'undefined'
                && cachedSecrets[secretKey] !== null) {
                return cachedSecrets[secretKey];
            }
            // purge the cache
            this.spydraCache.del(`${this.vaultName}-${this.normalizeKey(secretName)}`);
            const secrets = await this.getAll(this.normalizeKey(secretName));
            if (typeof secrets[secretKey] !== 'undefined'
                && secrets[secretKey] !== null) {
                return secrets[secretKey];
            }
            throw new Error(`Secret ${secretKey} not found in ${secretName}`);
        };
        this.getAll = async (secretName, cacheSecret = true) => {
            const cachedSecrets = this.spydraCache.get(`${this.vaultName}-${this.normalizeKey(secretName)}`) || {};
            if (lodash_1.default.isEmpty(cachedSecrets) === false && cacheSecret) {
                return cachedSecrets;
            }
            let credentials = new identity_1.DefaultAzureCredential();
            if (this.applicationId && this.applicationSecret && this.tenantId) {
                credentials = new identity_1.ClientSecretCredential(this.tenantId, this.applicationId, this.applicationSecret);
            }
            const secretClient = new keyvault_secrets_1.SecretClient(`https://${this.vaultName}.vault.azure.net`, credentials);
            const secrets = await secretClient.getSecret(this.normalizeKey(secretName)).then((secret) => (JSON.parse(secret.value ?? '')));
            this.spydraCache.set(`${this.vaultName}-${this.normalizeKey(secretName)}`, secrets, 60 * 60);
            return secrets;
        };
        this.exist = async (secretName) => {
            let credentials = new identity_1.DefaultAzureCredential();
            if (this.applicationId && this.applicationSecret && this.tenantId) {
                credentials = new identity_1.ClientSecretCredential(this.tenantId, this.applicationId, this.applicationSecret);
            }
            const secretClient = new keyvault_secrets_1.SecretClient(`https://${this.vaultName}.vault.azure.net`, credentials);
            let secretExists = false;
            try {
                const secret = await secretClient.getSecret(this.normalizeKey(secretName));
                secretExists = secret !== null;
            }
            catch {
                secretExists = false;
            }
            return secretExists;
        };
        this.delete = async (secretName) => {
            let credentials = new identity_1.DefaultAzureCredential();
            if (this.applicationId && this.applicationSecret && this.tenantId) {
                credentials = new identity_1.ClientSecretCredential(this.tenantId, this.applicationId, this.applicationSecret);
            }
            const secretClient = new keyvault_secrets_1.SecretClient(`https://${this.vaultName}.vault.azure.net`, credentials);
            await secretClient.beginDeleteSecret(this.normalizeKey(secretName));
            this.spydraCache.del(`${this.vaultName}-${this.normalizeKey(secretName)}`);
        };
        this.create = async (secretName, secrets, options) => {
            let credentials = new identity_1.DefaultAzureCredential();
            if (this.applicationId && this.applicationSecret && this.tenantId) {
                credentials = new identity_1.ClientSecretCredential(this.tenantId, this.applicationId, this.applicationSecret);
            }
            const secretClient = new keyvault_secrets_1.SecretClient(`https://${this.vaultName}.vault.azure.net`, credentials);
            await secretClient.setSecret(this.normalizeKey(secretName), JSON.stringify(secrets), { contentType: 'json', ...options });
            this.spydraCache.set(`${this.vaultName}-${this.normalizeKey(secretName)}`, secrets, 60 * 60);
            return secrets;
        };
        this.deleteMany = async (searchString) => {
            let credentials = new identity_1.DefaultAzureCredential();
            if (this.applicationId && this.applicationSecret && this.tenantId) {
                credentials = new identity_1.ClientSecretCredential(this.tenantId, this.applicationId, this.applicationSecret);
            }
            const secretClient = new keyvault_secrets_1.SecretClient(`https://${this.vaultName}.vault.azure.net`, credentials);
            for await (const secretProperties of secretClient.listPropertiesOfSecrets()) {
                // do something with properties
                if (secretProperties.name.startsWith(searchString)) {
                    await secretClient.beginDeleteSecret(secretProperties.name);
                }
            }
        };
        this.update = async (secretName, secrets, options) => {
            let credentials = new identity_1.DefaultAzureCredential();
            if (this.applicationId && this.applicationSecret && this.tenantId) {
                credentials = new identity_1.ClientSecretCredential(this.tenantId, this.applicationId, this.applicationSecret);
            }
            const secretClient = new keyvault_secrets_1.SecretClient(`https://${this.vaultName}.vault.azure.net`, credentials);
            await secretClient.setSecret(this.normalizeKey(secretName), JSON.stringify(secrets), { contentType: 'json', ...options });
            this.spydraCache.set(`${this.vaultName}-${this.normalizeKey(secretName)}`, secrets, 60 * 60);
            return secrets;
        };
        this.generatePassword = async (length = 22) => {
            throw new Error('Method not implemented.');
        };
        this.existenceCheck = async () => {
            let credentials = new identity_1.DefaultAzureCredential();
            if (this.applicationId && this.applicationSecret && this.tenantId) {
                credentials = new identity_1.ClientSecretCredential(this.tenantId, this.applicationId, this.applicationSecret);
            }
            if (!this.vaultName) {
                throw new Error('Vault is required for azure secret provider');
            }
            const compoundResult = {
                result: false,
                message: '',
                errorCode: 0,
            };
            try {
                const newVaultUrl = this.vaultName;
                const client = new keyvault_secrets_1.SecretClient(newVaultUrl, credentials);
                await client.listPropertiesOfSecrets().next();
                compoundResult.result = true;
            }
            catch (error) {
                compoundResult.result = false;
                if (error?.name === 'RestError' && error?.code === 'Forbidden') {
                    compoundResult.message = 'Unable to access the vault. Please check the access policy of the vault';
                    compoundResult.errorCode = 403;
                }
                else if (error?.name === 'RestError' && error?.code === 'ServiceUnavailable') {
                    compoundResult.message = 'Unable to verify the existence of the vault. Please check the vault URL and try again later';
                    compoundResult.errorCode = 503;
                }
                else if (error?.name === 'RestError' && error?.code === 'ENOTFOUND') {
                    compoundResult.message = 'Unable to verify the existence of the vault. Please check the vault URL and try again.';
                    compoundResult.errorCode = 404;
                }
                else {
                    compoundResult.message = 'Unable to verify the existence of the vault. Please check the vault URL';
                    compoundResult.errorCode = 500;
                }
            }
            return compoundResult;
        };
        this.vaultName = this.getVaultName(azureConfig.vault, azureConfig.region);
        this.region = azureConfig.region;
        this.accessKey = azureConfig.accessKey;
        this.secretKey = azureConfig.secretKey;
        this.shouldAutoFetchSecret = azureConfig.autoFetchSecret;
        this.autoFetchSecretProvider = azureConfig.autoFetchSecretProvider;
        this.autoFetchSecretVault = azureConfig.autoFetchSecretVault;
        this.autoFetchSecretName = azureConfig.autoFetchSecretName;
        this.logger = logger;
        this.applicationId = azureConfig.applicationId;
        this.applicationSecret = azureConfig.applicationSecret;
        this.tenantId = azureConfig.tenantId;
    }
}
exports.AzureKeyVaultSecretProvider = AzureKeyVaultSecretProvider;
//# sourceMappingURL=AzureKeyVaultSecretProvider.js.map