"use strict";
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.authApis = void 0;
const perm_1 = require("./perm");
const apiMiddleware_1 = require("./apiMiddleware");
const const_1 = require("./const");
const adminApis_1 = require("./adminApis");
const middlewares_1 = require("./middlewares");
const auth_1 = require("./auth");
const config_1 = require("./config");
const events_1 = __importDefault(require("./events"));
const misc_1 = require("./misc");
const ongoingLogins = {}; // store data that doesn't fit session object
const keepSessionAlive = (0, config_1.defineConfig)('keep_session_alive', true);
const refresh_session = async ({}, ctx) => {
    const username = (0, auth_1.getCurrentUsername)(ctx);
    const isAdmin = (0, adminApis_1.ctxAdminAccess)(ctx) || undefined;
    return !ctx.session ? new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR) : {
        username,
        expandedUsername: Array.from((0, perm_1.expandUsername)(username)),
        isAdmin,
        adminUrl: isAdmin && ctx.state.revProxyPath + const_1.ADMIN_URI,
        canChangePassword: (0, perm_1.accountCanChangePassword)(ctx.state.account),
        requireChangePassword: ctx.state.account?.require_password_change,
        exp: username && keepSessionAlive.get() ? new Date(Date.now() + middlewares_1.sessionDuration.compiled()) : undefined,
        accountExp: ctx.state.account?.expire,
    };
};
exports.authApis = {
    async login({ username, password }, ctx) {
        if (!username)
            return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
        if (!ctx.session)
            return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
        try {
            const account = await (0, auth_1.clearTextLogin)(ctx, username, password, 'api');
            if (!account)
                return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
            await (0, auth_1.setLoggedIn)(ctx, account.username);
        }
        catch (e) {
            return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED, String(e));
        }
        return {
            redirect: ctx.state.account?.redirect,
            ...await refresh_session({}, ctx)
        };
    },
    async loginSrp1({ username }, ctx) {
        (0, misc_1.apiAssertTypes)({ string: { username } });
        if (!username)
            return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
        const account = (0, perm_1.getAccount)(username);
        if (!ctx.session)
            return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
        if (account?.plugin?.auth) // tell client to do clear-text login, before firing attemptingLogin, before triggering anti-brute
            return new apiMiddleware_1.ApiError(const_1.HTTP_METHOD_NOT_ALLOWED);
        if ((await events_1.default.emitAsync('attemptingLogin', { ctx, username }))?.isDefaultPrevented())
            return;
        if (!account || !(0, perm_1.accountCanLogin)(account)) { // TODO simulate fake account to prevent knowing valid usernames
            ctx.logExtra({ u: username });
            ctx.state.dontLog = false; // log even if log_api is false
            return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED, account && (0, perm_1.accountIsDisabled)(account) ? 'Account disabled' : undefined);
        }
        if ((0, middlewares_1.failAllowNet)(ctx, account))
            return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
        try {
            const { srpServer, ...rest } = await (0, auth_1.srpServerStep1)(account);
            const sid = Math.random();
            ongoingLogins[sid] = srpServer;
            setTimeout(() => delete ongoingLogins[sid], 60_000);
            ctx.session.loggingIn = { username, sid }; // temporarily store until process is complete
            return rest;
        }
        catch (code) {
            return new apiMiddleware_1.ApiError(code);
        }
    },
    async loginSrp2({ pubKey, proof }, ctx) {
        if (!ctx.session)
            return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
        if (!ctx.session.loggingIn)
            return new apiMiddleware_1.ApiError(const_1.HTTP_CONFLICT);
        const { username, sid } = ctx.session.loggingIn;
        delete ctx.session.loggingIn;
        const step1 = ongoingLogins[sid];
        if (!step1)
            return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
        try {
            const M2 = await step1.step2(BigInt(pubKey), BigInt(proof))
                .catch(() => { throw ''; });
            await (0, auth_1.setLoggedIn)(ctx, username);
            return {
                proof: String(M2),
                redirect: ctx.state.account?.redirect,
                ...await refresh_session({}, ctx)
            };
        }
        catch (e) {
            ctx.logExtra({ u: username });
            ctx.state.dontLog = false; // log even if log_api is false
            events_1.default.emit('failedLogin', { ctx, username });
            return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED, e ? String(e) : undefined);
        }
        finally {
            delete ongoingLogins[sid];
        }
    },
    // this api is here for consistency, but frontend is actually using
    async logout({}, ctx) {
        if (!ctx.session)
            return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
        await (0, auth_1.setLoggedIn)(ctx, false);
        // 401 is a convenient code for OK: the browser clears a possible http authentication (hopefully), and Admin automatically triggers login dialog
        return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
    },
    refresh_session,
    async change_srp({ username, salt, verifier }, ctx) {
        const a = username && (0, perm_1.getAccount)(username);
        const can = a && ((0, adminApis_1.ctxAdminAccess)(ctx) || username === (0, auth_1.getCurrentUsername)(ctx) && (0, perm_1.accountCanChangePassword)(a));
        if (!can)
            return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
        if (!salt || !verifier)
            return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'missing parameters');
        await (0, perm_1.updateAccount)(a, a => (0, perm_1.saveSrpInfo)(a, salt, verifier));
        delete a.require_password_change;
        return {};
    }
};
