"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Crypt = void 0;
const crypto = require("crypto");
const bcrypt = require("bcryptjs");
const jsonwebtoken = require("jsonwebtoken");
const Config_1 = require("./Config");
const openflow_api_1 = require("@openiap/openflow-api");
const Logger_1 = require("./Logger");
class Crypt {
    static rootUser() {
        const result = new openflow_api_1.User();
        result._type = "user";
        result.name = "root";
        result.username = "root";
        result._id = openflow_api_1.WellknownIds.root;
        result.roles = [];
        result.roles.push(new openflow_api_1.Rolemember("admins", openflow_api_1.WellknownIds.admins));
        return result;
    }
    static rootToken() {
        return Crypt.createToken(this.rootUser(), Config_1.Config.shorttoken_expires_in);
    }
    static async SetPassword(user, password, parent) {
        const span = Logger_1.Logger.otel.startSubSpan("Crypt.SetPassword", parent);
        try {
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(user))
                throw new Error("user is mandatody");
            if (openflow_api_1.NoderedUtil.IsNullEmpty(password))
                throw new Error("password is mandatody");
            user.passwordhash = await Crypt.hash(password);
            if (!(this.ValidatePassword(user, password, span))) {
                throw new Error("Failed validating password after hasing");
            }
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    static async ValidatePassword(user, password, parent) {
        const span = Logger_1.Logger.otel.startSubSpan("Crypt.ValidatePassword", parent);
        try {
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(user))
                throw new Error("user is mandatody");
            if (openflow_api_1.NoderedUtil.IsNullEmpty(password))
                throw new Error("password is mandatody");
            return await Crypt.compare(password, user.passwordhash, span);
        }
        catch (error) {
            span === null || span === void 0 ? void 0 : span.recordException(error);
            throw error;
        }
        finally {
            Logger_1.Logger.otel.endSpan(span);
        }
    }
    static encrypt(text) {
        let iv = crypto.randomBytes(Crypt.iv_length);
        if (openflow_api_1.NoderedUtil.IsNullEmpty(Crypt.encryption_key))
            Crypt.encryption_key = Config_1.Config.aes_secret.substr(0, 32);
        let cipher = crypto.createCipheriv('aes-256-gcm', Buffer.from(Crypt.encryption_key), iv);
        let encrypted = cipher.update(text);
        encrypted = Buffer.concat([encrypted, cipher.final()]);
        const authTag = cipher.getAuthTag();
        return iv.toString("hex") + ":" + encrypted.toString("hex") + ":" + authTag.toString("hex");
    }
    static decrypt(text) {
        let textParts = text.split(":");
        let iv = Buffer.from(textParts.shift(), "hex");
        let encryptedText = Buffer.from(textParts.shift(), "hex");
        let authTag = null;
        if (textParts.length > 0)
            authTag = Buffer.from(textParts.shift(), "hex");
        let decrypted;
        if (openflow_api_1.NoderedUtil.IsNullEmpty(Crypt.encryption_key))
            Crypt.encryption_key = Config_1.Config.aes_secret.substr(0, 32);
        if (authTag != null) {
            let decipher = crypto.createDecipheriv('aes-256-gcm', Buffer.from(Crypt.encryption_key), iv);
            decipher.setAuthTag(authTag);
            decrypted = decipher.update(encryptedText);
            decrypted = Buffer.concat([decrypted, decipher.final()]);
        }
        else {
            let decipher2 = crypto.createDecipheriv("aes-256-cbc", Buffer.from(Crypt.encryption_key), iv);
            decrypted = decipher2.update(encryptedText);
            decrypted = Buffer.concat([decrypted, decipher2.final()]);
        }
        return decrypted.toString();
    }
    static async hash(password) {
        return new Promise(async (resolve, reject) => {
            try {
                bcrypt.hash(password, Crypt.bcrypt_salt_rounds, async (error, hash) => {
                    if (error) {
                        return reject(error);
                    }
                    resolve(hash);
                });
            }
            catch (error) {
                reject(error);
            }
        });
    }
    static async compare(password, passwordhash, parent) {
        const span = Logger_1.Logger.otel.startSubSpan("Crypt.compare", parent);
        return new Promise(async (resolve, reject) => {
            try {
                if (openflow_api_1.NoderedUtil.IsNullEmpty(password)) {
                    span === null || span === void 0 ? void 0 : span.recordException("Password cannot be empty");
                    return reject("Password cannot be empty");
                }
                if (openflow_api_1.NoderedUtil.IsNullEmpty(passwordhash)) {
                    span === null || span === void 0 ? void 0 : span.recordException("Passwordhash cannot be empty");
                    return reject("Passwordhash cannot be empty");
                }
                bcrypt.compare(password, passwordhash, async (error, res) => {
                    if (error) {
                        span === null || span === void 0 ? void 0 : span.recordException(error);
                        Logger_1.Logger.otel.endSpan(span);
                        return reject(error);
                    }
                    Logger_1.Logger.otel.endSpan(span);
                    resolve(res);
                });
            }
            catch (error) {
                span === null || span === void 0 ? void 0 : span.recordException(error);
                reject(error);
                Logger_1.Logger.otel.endSpan(span);
            }
        });
    }
    static createToken(item, expiresIn) {
        const user = new openflow_api_1.TokenUser();
        user._type = item._type;
        user._id = item._id;
        user.impostor = item.impostor;
        user.name = item.name;
        user.username = item.username;
        user.roles = item.roles;
        user.customerid = item.customerid;
        user.selectedcustomerid = item.selectedcustomerid;
        user.dblocked = item.dblocked;
        if (openflow_api_1.NoderedUtil.IsNullEmpty(Crypt.encryption_key))
            Crypt.encryption_key = Config_1.Config.aes_secret.substr(0, 32);
        const key = Crypt.encryption_key;
        if (openflow_api_1.NoderedUtil.IsNullEmpty(Config_1.Config.aes_secret))
            throw new Error("Config missing aes_secret");
        if (openflow_api_1.NoderedUtil.IsNullEmpty(key))
            throw new Error("Config missing aes_secret");
        return jsonwebtoken.sign({ data: user }, key, { expiresIn: expiresIn }); // 60 (seconds), "2 days", "10h", "7d"
    }
    static async verityToken(token) {
        if (openflow_api_1.NoderedUtil.IsNullEmpty(token)) {
            throw new Error('jwt must be provided');
        }
        if (openflow_api_1.NoderedUtil.IsNullEmpty(Crypt.encryption_key))
            Crypt.encryption_key = Config_1.Config.aes_secret.substr(0, 32);
        const o = jsonwebtoken.verify(token, Crypt.encryption_key);
        let impostor = null;
        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(o) && !openflow_api_1.NoderedUtil.IsNullUndefinded(o.data) && !openflow_api_1.NoderedUtil.IsNullEmpty(o.data._id)) {
            if (!openflow_api_1.NoderedUtil.IsNullEmpty(o.data.impostor)) {
                impostor = o.data.impostor;
            }
        }
        if (!openflow_api_1.NoderedUtil.IsNullUndefinded(o) && !openflow_api_1.NoderedUtil.IsNullUndefinded(o.data) && !openflow_api_1.NoderedUtil.IsNullEmpty(o.data._id) && o.data._id != openflow_api_1.WellknownIds.root) {
            var id = o.data._id;
            o.data = await Logger_1.Logger.DBHelper.FindById(o.data._id, token, null);
            if (openflow_api_1.NoderedUtil.IsNullUndefinded(o) || openflow_api_1.NoderedUtil.IsNullUndefinded(o.data)) {
                throw new Error("Token signature valid, but unable to find user with id " + id);
            }
        }
        if (!openflow_api_1.NoderedUtil.IsNullEmpty(impostor))
            o.data.impostor = impostor;
        return openflow_api_1.TokenUser.assign(o.data);
    }
    static decryptToken(token) {
        if (openflow_api_1.NoderedUtil.IsNullEmpty(Crypt.encryption_key))
            Crypt.encryption_key = Config_1.Config.aes_secret.substr(0, 32);
        return jsonwebtoken.verify(token, Crypt.encryption_key);
    }
}
exports.Crypt = Crypt;
Crypt.encryption_key = null; // must be 256 bytes (32 characters))
Crypt.iv_length = 16; // for AES, this is always 16
Crypt.bcrypt_salt_rounds = 12;
//# sourceMappingURL=Crypt.js.map