"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const types_1 = require("@deepstream/types");
const uuid_1 = require("uuid");
const utils_1 = require("../../../utils/utils");
const STRING = 'string';
class StorageBasedAuthentication extends types_1.DeepstreamPlugin {
    /**
    * Creates the class, reads and validates the users.json file
    */
    constructor(settings, services) {
        super();
        this.settings = settings;
        this.services = services;
        this.description = `Storage using table: ${this.settings.table}`;
        this.logger = this.services.logger.getNameSpace('STORAGE_AUTH');
        this.hashSettings = {
            iterations: this.settings.iterations,
            keyLength: this.settings.keyLength,
            algorithm: this.settings.hash
        };
    }
    async whenReady() {
        await this.services.storage.whenReady();
    }
    /**
    * Main interface. Authenticates incoming connections
    */
    async isValidUser(connectionData, authData) {
        const missingUsername = typeof authData.username !== STRING;
        const missingPassword = typeof authData.password !== STRING;
        if (missingPassword || missingUsername) {
            if (this.settings.reportInvalidParameters) {
                return {
                    isValid: false,
                    clientData: { error: `missing authentication parameters: ${missingUsername && 'username'} ${missingPassword && 'password'}` }
                };
            }
            else {
                return null;
            }
        }
        let userData;
        const storageId = `${this.settings.table}/${authData.username}`;
        try {
            userData = await new Promise((resolve, reject) => this.services.storage.get(storageId, (err, version, data) => err ? reject(err) : resolve(data)));
        }
        catch (err) {
            this.logger.error(types_1.EVENT.ERROR, `Error retrieving user from storage ${JSON.stringify(err)}`);
            return {
                isValid: false,
                clientData: { error: 'Error retrieving user from storage' }
            };
        }
        if (userData === null) {
            if (this.settings.createUser) {
                this.logger.info(types_1.EVENT.REGISTERING_USER, `Adding new user ${authData.username}`);
                const { hash, salt } = await utils_1.createHash(authData.password, this.hashSettings);
                const clientData = {
                    id: uuid_1.v4(),
                };
                const serverData = {
                    created: Date.now()
                };
                return await new Promise((resolve, reject) => this.services.storage.set(storageId, 1, {
                    username: authData.username,
                    password: hash.toString('ascii') + salt,
                    clientData,
                    serverData
                }, () => resolve({
                    isValid: true,
                    id: clientData.id,
                    clientData,
                    serverData
                })));
            }
            return null;
        }
        const expectedHash = userData.password.substr(0, this.settings.keyLength);
        const { hash: actualHash } = await utils_1.createHash(authData.password, this.hashSettings, userData.password.substr(this.settings.keyLength));
        if (expectedHash === actualHash.toString('ascii')) {
            return {
                isValid: true,
                id: userData.clientData.id,
                serverData: userData.serverData || null,
                clientData: userData.clientData || null,
            };
        }
        return { isValid: false };
    }
}
exports.StorageBasedAuthentication = StorageBasedAuthentication;
//# sourceMappingURL=storage-based-authentication.js.map