import { AuthPermissionError, ForbiddenError, UnimplementedError } from '@tamanu/errors';
import { getAbilityForUser, getPermissionsForRoles } from './rolesToPermissions';
// copied from casl source as it's not exported directly
// (we could use casl's ForbiddenError.throwUnlessCan function except there's some
// strange error going on where the export appears to strip properties assigned via
// Object.defineProperty - probably straightforward enough to find a workaround, but
// we only need this one function out of it so it's not worth it.
export function getSubjectName(subject) {
    if (!subject || typeof subject === 'string') {
        return subject;
    }
    const Type = typeof subject === 'object' ? subject.constructor : subject;
    return Type.modelName || Type.name;
}
export async function constructPermission(req, res, next) {
    try {
        // eslint-disable-next-line require-atomic-updates
        req.ability = await getAbilityForUser(req.models, req.user);
        next();
    } catch (e) {
        next(e);
    }
}
const checkIfHasPermission = (req, action, subject, field = '')=>{
    if (req.flagPermissionChecked) {
        req.flagPermissionChecked();
    }
    // allow a null permission to let things through - this means all endpoints
    // still need an explicit permission check, even if it's a null one!
    if (!action) {
        return;
    }
    const { ability } = req;
    if (!ability) {
        throw new AuthPermissionError(`${action} ${subject}`);
    }
    return ability.can(action, subject, field);
};
// this middleware goes at the top of the middleware stack
export function ensurePermissionCheck(req, res, next) {
    const originalResSend = res.send;
    req.checkPermission = (action, subject)=>{
        const hasPermission = checkIfHasPermission(req, action, subject);
        if (!hasPermission) {
            const rule = req.ability.relevantRuleFor(action, subject);
            const reason = rule && rule.reason || `No permission to perform action "${action}" on "${getSubjectName(subject)}"`;
            throw new ForbiddenError(reason);
        }
    };
    req.checkForOneOfPermissions = (actions, subject)=>{
        const permissionChecks = actions.map((action)=>checkIfHasPermission(req, action, subject));
        const hasPermission = permissionChecks.some(Boolean);
        if (!hasPermission) {
            const reason = `No permission to perform any of actions "${actions.join(', ')}" on "${getSubjectName(subject)}"`;
            throw new ForbiddenError(reason);
        }
    };
    req.checkListOrReadPermission = (subject)=>{
        req.checkForOneOfPermissions([
            'list',
            'read'
        ], subject);
    };
    req.flagPermissionChecked = ()=>{
        res.send = originalResSend;
    };
    res.send = ()=>{
        res.send = originalResSend;
        throw new UnimplementedError('No permission check was implemented for this endpoint.');
    };
    next();
}
// eslint-disable-next-line no-unused-vars
export async function getPermissions(req, res, _next) {
    const { user, models } = req;
    const permissions = await getPermissionsForRoles(models, user.role);
    res.send({
        permissions
    });
}

//# sourceMappingURL=middleware.js.map