'use strict';
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.authorize = authorize;
exports.checkPermissions = checkPermissions;
const validation = __importStar(require("../validation/check_permissions.js"));
const passport_1 = __importDefault(require("passport"));
const passport_local_1 = require("passport-local");
const passport_http_1 = require("passport-http");
const util = __importStar(require("util"));
const userFunctions = __importStar(require("./user.js"));
const cbFindValidateUsers = util.callbackify(userFunctions.findAndValidateUser);
const hdbTerms = __importStar(require("../utility/hdbTerms.js"));
const tokenAuthentication = __importStar(require("./tokenAuthentication.js"));
const hdbError_ts_1 = require("../utility/errors/hdbError.js");
const auth_ts_1 = require("./auth.js");
passport_1.default.use(new passport_local_1.Strategy(function (username, password, done) {
    cbFindValidateUsers(username, password, done);
}));
passport_1.default.use(new passport_http_1.BasicStrategy(function (username, password, done) {
    cbFindValidateUsers(username, password, done);
}));
passport_1.default.serializeUser(function (user, done) {
    done(null, user);
});
passport_1.default.deserializeUser(function (user, done) {
    done(null, user);
});
const INTERNAL_USER_HEADER = 'x-harper-internal-pre-auth-user';
function authorize(req, res, next) {
    if (req.raw?.user != undefined) {
        return next(null, req.raw.user);
    }
    // On Bun, Harper's auth middleware passes pre-authenticated users via this internal header.
    // bunDelegateToNodeServer strips it from real network requests before injecting into Fastify,
    // so it is only safe to trust under Bun — on Node.js the raw socket path delivers headers
    // directly to Fastify with no stripping, so a forged header could bypass auth.
    if (typeof globalThis.Bun !== 'undefined') {
        const preAuthUser = req.headers?.[INTERNAL_USER_HEADER];
        if (preAuthUser)
            return next(null, JSON.parse(preAuthUser));
        // No pre-auth header: auth.ts didn't run for this port (ops API). Mirror what Node.js does via
        // baseRequest — build a shim request and call authentication() so AUTHORIZE_LOCAL can apply.
        const shimRequest = {
            headers: { asObject: Object.assign({}, req.headers) },
            ip: req.socket?.remoteAddress ?? '',
            isOperationsServer: true,
            method: req.method,
            url: req.url,
            pathname: (req.url ?? '/').split('?')[0],
            authorized: undefined,
            mtlsConfig: undefined,
            peerCertificate: { subject: null },
            _nodeRequest: null,
            _nodeResponse: null,
        };
        let nextCalled = false;
        return (0, auth_ts_1.authentication)(shimRequest, (request) => {
            nextCalled = true;
            if (request.user)
                return next(null, request.user);
            req.raw.user = null;
            return authorize(req, res, next);
        }).then((response) => {
            if (nextCalled)
                return response;
            if (response?.status === -1) {
                req.raw.user = null;
                return authorize(req, res, next);
            }
            const body = typeof response?.body === 'string' ? JSON.parse(response.body) : (response?.body ?? {});
            return next(new Error(body.error ?? body));
        }, (error) => next(error));
    }
    if (req.raw?.user === undefined && req.raw?.baseRequest) {
        let nextCalled = false;
        return (0, auth_ts_1.authentication)(req.raw?.baseRequest, (request) => {
            nextCalled = true;
            if (request.user) {
                req.raw.user = request.user;
                return next(null, req.raw.user);
            }
            else {
                req.raw.user = null; // don't fall in this branch again
                return authorize(req, res, next);
            }
        }).then((response) => {
            if (nextCalled) {
                return response;
            }
            if (response?.status === -1) {
                // authentication declined (e.g. refresh token) — fall through to the
                // Bearer/Basic handling below
                req.raw.user = null;
                return authorize(req, res, next);
            }
            const body = JSON.parse(response.body);
            return next(new Error(body.error ?? body));
        }, (error) => {
            return next(error);
        });
    }
    let strategy;
    let token;
    if (req.headers?.authorization) {
        let splitAuthHeader = req.headers.authorization.split(' ');
        strategy = splitAuthHeader[0];
        token = splitAuthHeader[1];
    }
    function handleResponse(err, user) {
        if (err) {
            return next(err);
        }
        if (!user) {
            return next(new hdbError_ts_1.AccessViolation());
        }
        return next(null, user);
    }
    switch (strategy) {
        case 'Basic':
            passport_1.default.authenticate('basic', { session: false }, (err, user) => {
                handleResponse(err, user);
            })(req, res, next);
            break;
        case 'Bearer':
            if (req.body?.operation && req.body.operation === hdbTerms.OPERATIONS_ENUM.REFRESH_OPERATION_TOKEN) {
                tokenAuthentication
                    .validateRefreshToken(token)
                    .then((user) => {
                    req.body.refresh_token = token;
                    next(null, user);
                })
                    .catch((e) => {
                    next(e);
                });
            }
            else {
                tokenAuthentication
                    .validateOperationToken(token)
                    .then((user) => {
                    next(null, user);
                })
                    .catch((e) => {
                    next(e);
                });
            }
            break;
        default:
            passport_1.default.authenticate('local', { session: false }, function (err, user) {
                handleResponse(err, user);
            })(req, res, next);
            break;
    }
}
function checkPermissions(checkPermissionObj, callback) {
    let validationResults = validation.default
        ? validation.default(checkPermissionObj)
        : validation(checkPermissionObj);
    if (validationResults) {
        callback(validationResults);
        return;
    }
    let authoriziationObj = {
        authorized: true,
        messages: [],
    };
    let role = checkPermissionObj.user.role;
    if (!role?.permission) {
        return callback('Invalid role');
    }
    let permission = JSON.parse(role.permission);
    if (permission.super_user) {
        return callback(null, authoriziationObj);
    }
    if (!permission[checkPermissionObj.schema]) {
        authoriziationObj.authorized = false;
        authoriziationObj.messages.push(`Not authorized to access ${checkPermissionObj.schema} schema`);
        return callback(null, authoriziationObj);
    }
    if (!permission[checkPermissionObj.schema].tables[checkPermissionObj.table]) {
        authoriziationObj.authorized = false;
        authoriziationObj.messages.push(`Not authorized to access ${checkPermissionObj.table} table`);
        return callback(null, authoriziationObj);
    }
    if (!permission[checkPermissionObj.schema].tables[checkPermissionObj.table][checkPermissionObj.operation]) {
        authoriziationObj.authorized = false;
        authoriziationObj.messages.push(`Not authorized to access ${checkPermissionObj.operation} on ${checkPermissionObj.table} table`);
        return callback(null, authoriziationObj);
    }
    if (permission[checkPermissionObj.schema].tables[checkPermissionObj.table].attribute_permissions &&
        !checkPermissionObj.attributes) {
        authoriziationObj.authorized = false;
        authoriziationObj.messages.push(`${checkPermissionObj.schema}.${checkPermissionObj.table} has attribute permissions. Missing attributes to validate`);
        return callback(null, authoriziationObj);
    }
    if (permission[checkPermissionObj.schema].tables[checkPermissionObj.table].attribute_permissions &&
        checkPermissionObj.attributes) {
        let restrictedAttrs = permission[checkPermissionObj.schema].tables[checkPermissionObj.table].attribute_permissions;
        for (let rAttr in restrictedAttrs) {
            if (checkPermissionObj.attributes.indexOf(restrictedAttrs[rAttr].attribute_name) > -1 &&
                !restrictedAttrs[rAttr][checkPermissionObj.operation]) {
                authoriziationObj.authorized = false;
                authoriziationObj.messages.push(`Not authorized to ${checkPermissionObj.operation} ${restrictedAttrs[rAttr].attribute_name} `);
            }
        }
    }
    return callback(null, authoriziationObj);
}
//# sourceMappingURL=fastifyAuth.js.map