"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AWSSecretManagerProvider = void 0;
const node_cache_1 = require("../../node-cache");
const aws_sdk_1 = __importDefault(require("aws-sdk"));
const logger_1 = require("../../../logger");
const identity_1 = require("@azure/identity");
const keyvault_secrets_1 = require("@azure/keyvault-secrets");
const lodash_1 = __importDefault(require("lodash"));
class AWSSecretManagerProvider {
    constructor(awsConfig, logger = logger_1.logger) {
        this.spydraCache = node_cache_1.NodeCache.getInstance();
        this.logger = logger_1.logger;
        this.normalizeKey = (key) => key.replace(/[^a-zA-Z0-9]/g, '-');
        /**
         * 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 () => {
            if (this.shouldAutoFetchSecret
                && this.autoFetchSecretProvider === 'aws') {
                if (lodash_1.default.isEmpty(this.region) && lodash_1.default.isEmpty(this.autoFetchSecretRegion)) {
                    throw new Error('Region is required for aws secret provider');
                }
                if (lodash_1.default.isEmpty(this.autoFetchSecretName)) {
                    throw new Error('Secret Name is required for aws secret provider');
                }
                const region = this.autoFetchSecretRegion || this.region;
                const cachedSecret = this.spydraCache.get(`${region}-${this.autoFetchSecretName}`);
                if (cachedSecret && lodash_1.default.isEmpty(cachedSecret) === false) {
                    this.accessKey = cachedSecret?.AWS_ACCESS_KEY_ID;
                    this.secretKey = cachedSecret?.AWS_SECRET_ACCESS_KEY;
                }
                else {
                    const secrets = await this.getAll(this.autoFetchSecretName, region);
                    this.accessKey = secrets.AWS_ACCESS_KEY_ID;
                    this.secretKey = secrets.AWS_SECRET_ACCESS_KEY;
                }
            }
            else if (this.shouldAutoFetchSecret
                && this.autoFetchSecretProvider === 'azure') {
                if (lodash_1.default.isEmpty(this.autoFetchSecretVault)) {
                    throw new Error('Vault is required for azure secret provider');
                }
                if (lodash_1.default.isEmpty(this.autoFetchSecretName)) {
                    throw new Error('Secret Name is required for azure secret provider');
                }
                const cachedSecret = this.spydraCache.get(`${this.autoFetchSecretVault}-${this.normalizeKey(this.autoFetchSecretName)}`);
                if (cachedSecret && lodash_1.default.isEmpty(cachedSecret) === false) {
                    this.accessKey = cachedSecret?.AWS_ACCESS_KEY_ID;
                    this.secretKey = cachedSecret?.AWS_SECRET_ACCESS_KEY;
                }
                else {
                    const credential = new identity_1.DefaultAzureCredential();
                    const secretClient = new keyvault_secrets_1.SecretClient(`https://${this.autoFetchSecretVault}.vault.azure.net`, credential);
                    const secrets = await secretClient.getSecret(this.normalizeKey(this.autoFetchSecretName)).then((value) => (JSON.parse(value.value ?? '')));
                    this.spydraCache.set(`${this.autoFetchSecretVault}-${this.normalizeKey(this.autoFetchSecretName)}`, secrets, 60 * 60);
                    this.accessKey = secrets.AWS_ACCESS_KEY_ID;
                    this.secretKey = secrets.AWS_SECRET_ACCESS_KEY;
                }
            }
        };
        this.getAll = async (secretName, overrideRegion) => {
            const region = overrideRegion || this.region;
            let cachedSecret = this.spydraCache.get(`${region}-${secretName}`);
            if (typeof cachedSecret !== 'undefined'
                && cachedSecret !== null) {
                return cachedSecret;
            }
            // skip fetching if the secret name is the same as the auto fetch secret name
            if (this.autoFetchSecretName !== secretName) {
                await this.autoFetchSecret();
            }
            const config = {
                region: region,
                accessKeyId: this.accessKey,
                secretAccessKey: this.secretKey,
            };
            const client = new aws_sdk_1.default.SecretsManager(config);
            const result = await client
                .getSecretValue({ SecretId: secretName })
                .promise();
            // eslint-disable-next-line unicorn/no-negated-condition
            if (typeof result.SecretString !== 'undefined') {
                cachedSecret = JSON.parse(result.SecretString ?? '');
            }
            else {
                const buffer = Buffer.from(result.SecretBinary, 'base64');
                cachedSecret = JSON.parse(buffer.toString('ascii'));
            }
            this.spydraCache.set(`${region}-${secretName}`, cachedSecret);
            return cachedSecret;
        };
        this.get = async (secretName, secretKey) => {
            const cachedSecret = this.spydraCache.get(`${this.region}-${secretName}`) || {};
            if (lodash_1.default.isEmpty(cachedSecret) === false
                && typeof cachedSecret[secretKey] !== 'undefined'
                // eslint-disable-next-line unicorn/no-negated-condition, valid-typeof, no-constant-binary-expression
                && typeof cachedSecret[secretKey] !== null) {
                return cachedSecret[secretKey];
            }
            // purge the cache
            this.spydraCache.del(`${this.region}-${secretName}`);
            const secrets = await this.getAll(`${secretName}`) || {};
            if (typeof secrets[secretKey] !== 'undefined'
                && secrets[secretKey] !== null) {
                return secrets[secretKey];
            }
            throw new Error(`Secret ${secretKey} not found in ${secretName}`);
        };
        this.exist = async (secretName) => {
            try {
                await this.autoFetchSecret();
                const config = {
                    region: this.region,
                    accessKeyId: this.accessKey,
                    secretAccessKey: this.secretKey,
                };
                const client = new aws_sdk_1.default.SecretsManager(config);
                await client.describeSecret({ SecretId: secretName }).promise();
                return true;
            }
            catch (error) {
                if (error.code === 'ResourceNotFoundException') {
                    this.logger.debug(`Secret ${secretName} does not exist`);
                    return false;
                }
                this.logger.debug(`Error checking secret ${secretName}: ${error}`);
                throw new Error(`Error checking secret ${secretName}`);
            }
        };
        this.delete = async (secretName) => {
            try {
                await this.autoFetchSecret();
                const config = {
                    region: this.region,
                    accessKeyId: this.accessKey,
                    secretAccessKey: this.secretKey,
                };
                const client = new aws_sdk_1.default.SecretsManager(config);
                await client.deleteSecret({ SecretId: secretName }).promise();
            }
            catch (error) {
                this.logger.error(`Cannot delete aws secret, Error: ${error}`);
            }
        };
        this.create = async (secretName, secrets) => {
            try {
                await this.autoFetchSecret();
                const config = {
                    region: this.region,
                    accessKeyId: this.accessKey,
                    secretAccessKey: this.secretKey,
                };
                const client = new aws_sdk_1.default.SecretsManager(config);
                const createPayload = {
                    Name: secretName,
                    SecretString: JSON.stringify(secrets),
                };
                // if (
                // this.shouldAutoFetchSecret
                // ) {
                createPayload.Tags = [{
                        Key: 'projectowner',
                        Value: 'spydrablockchain',
                    }];
                // }
                await client.createSecret(createPayload).promise();
                // update cache
                this.spydraCache.set(`${this.region}-${secretName}`, secrets);
                return secrets;
            }
            catch (error) {
                this.logger.error(`Cannot create aws secret, Error: ${error}`);
                throw error;
            }
        };
        this.deleteMany = async (searchString) => {
            try {
                await this.autoFetchSecret();
                const config = {
                    region: this.region,
                    accessKeyId: this.accessKey,
                    secretAccessKey: this.secretKey,
                };
                const client = new aws_sdk_1.default.SecretsManager(config);
                client.listSecrets({
                    Filters: [
                        {
                            Key: 'name',
                            Values: [searchString],
                        },
                    ],
                }, async (err, data) => {
                    if (data && data.SecretList && Array.isArray(data.SecretList)) {
                        for await (const secret of data.SecretList) {
                            if (secret.Name) {
                                try {
                                    client.deleteSecret({ SecretId: secret.Name }).promise();
                                }
                                catch (error) {
                                    this.logger.error(`Cannot delete aws secret, Error: ${error}`);
                                }
                            }
                        }
                    }
                });
            }
            catch (error) {
                this.logger.error(`Cannot delete aws secret, Error: ${error}`);
            }
        };
        this.update = async (secretName, secrets) => {
            try {
                await this.autoFetchSecret();
                const config = {
                    region: this.region,
                    accessKeyId: this.accessKey,
                    secretAccessKey: this.secretKey,
                };
                const client = new aws_sdk_1.default.SecretsManager(config);
                await client.updateSecret({
                    SecretId: secretName,
                    SecretString: JSON.stringify(secrets),
                }).promise();
                // update cache
                this.spydraCache.set(`${this.region}-${secretName}`, secrets);
                return secrets;
            }
            catch (error) {
                this.logger.error(`Cannot update aws secrets, Error: ${error}`);
                throw error;
            }
        };
        this.generatePassword = async (length) => {
            await this.autoFetchSecret();
            const lengthOfPassword = length || 22;
            const payload = {
                PasswordLength: lengthOfPassword,
                ExcludePunctuation: true,
            };
            const config = {
                region: this.region,
                accessKeyId: this.accessKey,
                secretAccessKey: this.secretKey,
            };
            const client = new aws_sdk_1.default.SecretsManager(config);
            const result = await client.getRandomPassword(payload).promise();
            if (!result?.RandomPassword) {
                this.logger.error(`Failed to create password for: ${payload}`);
                throw new Error('failed to create password');
            }
            return result.RandomPassword;
        };
        this.existenceCheck = async () => {
            const compoundResult = {
                result: false,
                message: '',
                errorCode: 0,
            };
            try {
                await this.autoFetchSecret();
                const config = {
                    accessKeyId: this.accessKey,
                    secretAccessKey: this.secretKey,
                    region: this.region,
                };
                const client = new aws_sdk_1.default.STS(config);
                await client.getCallerIdentity({}).promise().then((data) => {
                    if (lodash_1.default.isEmpty(data?.UserId) === false) {
                        compoundResult.result = true;
                    }
                }).catch((error) => {
                    if (error?.code === 'AccessDenied') {
                        compoundResult.message = 'Unable to access region. Please check the credentials';
                        compoundResult.errorCode = 403;
                    }
                    else if (error?.code === 'InvalidClientTokenId') {
                        compoundResult.message = 'Invalid access key. Please check the credentials';
                        compoundResult.errorCode = 401;
                    }
                    else if (error?.code === 'ExpiredToken') {
                        compoundResult.message = 'Expired token. Please check the credentials';
                        compoundResult.errorCode = 401;
                    }
                    else {
                        compoundResult.message = 'Unable to verify credentials';
                        compoundResult.errorCode = 500;
                    }
                    this.logger.error(`Unable to verify credentials: ${error}`);
                });
            }
            catch (error) {
                compoundResult.message = 'Unable to verify credentials. Please check the credentials';
                compoundResult.errorCode = 500;
                this.logger.error(`Unable to verify credentials: ${error}`);
            }
            return compoundResult;
        };
        this.region = awsConfig.region;
        this.accessKey = awsConfig.accessKey;
        this.secretKey = awsConfig.secretKey;
        this.shouldAutoFetchSecret = awsConfig.autoFetchSecret;
        this.autoFetchSecretProvider = awsConfig.autoFetchSecretProvider;
        this.autoFetchSecretVault = awsConfig.autoFetchSecretVault;
        this.autoFetchSecretName = awsConfig.autoFetchSecretName;
        this.autoFetchSecretRegion = awsConfig.autoFetchSecretRegion;
        this.logger = logger;
    }
}
exports.AWSSecretManagerProvider = AWSSecretManagerProvider;
//# sourceMappingURL=AWSSecretManagerProvider.js.map