"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.contextRoles = void 0;
const Session_1 = require("../store/Session");
const Config_1 = require("../lib/Config");
const Logger_1 = require("../lib/Logger");
const Gateway_1 = require("../remote-services/Gateway");
const logger = Logger_1.default.of('middleware:rbac');
/*
- methods: ['POST', 'PUT', 'PATCH', 'DELETE']
  paths:
      - /api/v1/container($|/.+)
  roles: ['admin']
*/
const RBAC = ({ roles, permissions }) => {
    // 把所有配置中的路径升级为RegExp对象
    permissions.forEach((perm) => {
        perm.paths = perm.paths.map((p) => {
            if (typeof p === 'string') {
                return new RegExp(p, 'i');
            }
            return p;
        });
    });
    return async (ctx, next) => {
        var _a;
        if (process.env.NOAUTH)
            return next();
        const r = await roles(ctx);
        for (const perm of permissions) {
            // 调用方法不匹配
            if (!((_a = perm.methods) === null || _a === void 0 ? void 0 : _a.includes(ctx.method)))
                continue;
            // 用户角色不存在
            if (perm.roles !== '*') {
                if (r.length === 0 || r.filter((r) => { var _a; return (_a = perm.roles) === null || _a === void 0 ? void 0 : _a.includes(r); }).length === 0)
                    continue;
            }
            let matched = false;
            for (const path of perm.paths) {
                matched = path.test(ctx.path);
                if (matched) {
                    break;
                }
            }
            if (matched) {
                logger.debug(`RBAC authorized: ${ctx.path} ${JSON.stringify(r)}`);
                return next();
            }
        }
        logger.debug(`RBAC denied: ${ctx.path} ${JSON.stringify(r)}`);
        ctx.status = 401;
    };
};
async function contextRoles(ctx, groupId) {
    const { ownerType, ownerId } = ctx.state;
    if (ownerId === undefined)
        return [];
    const roles = ['authenticated'];
    if (ownerType === 'user') {
        roles.push('authenticated.user');
        roles.push(...Config_1.default.rbac.roles.filter((r) => { var _a; return (_a = r.users) === null || _a === void 0 ? void 0 : _a.includes(ownerId); }).map((r) => r.name));
        const session = Session_1.default.get(`${ownerType}/${ownerId}`);
        await session.withPermissions();
        roles.push(...session.permissions);
        if (groupId) {
            const gateway = new Gateway_1.default();
            const session = Session_1.default.get(`${ownerType}/${ownerId}`);
            try {
                gateway.setAccessToken(session.gapperToken);
                const { super_admin, permissions } = await gateway.get({
                    path: 'v1/current-user/permissions',
                    query: { permission$: '{matrix.admin,matrix.view_video}', group_id: String(groupId) },
                });
                if (super_admin) {
                    roles.push('admin');
                }
                roles.push(...permissions.map((it) => `@${it.key}`));
            }
            catch (e) {
                logger.error(`contextRoles get permissions: ${session} ${JSON.stringify(e)}`);
            }
        }
    }
    else if (ownerType === 'device') {
        roles.push('authenticated.device');
        roles.push(...Config_1.default.rbac.roles.filter((r) => { var _a; return (_a = r.devices) === null || _a === void 0 ? void 0 : _a.includes(ownerId); }).map((r) => r.name));
        const session = Session_1.default.get(`${ownerType}/${ownerId}`);
        await session.withPermissions();
        roles.push(...session.permissions);
    }
    else if (ownerType === 'app') {
        roles.push('authenticated.app');
        roles.push(`app.${ownerId}`);
    }
    return [...new Set(roles)];
}
exports.contextRoles = contextRoles;
exports.default = RBAC({
    roles: async (ctx) => {
        const { ownerType, ownerId } = ctx.state;
        if (ownerId === undefined)
            return [];
        const roles = ['authenticated'];
        if (ownerType === 'user') {
            roles.push('authenticated.user');
            roles.push(...Config_1.default.rbac.roles.filter((r) => { var _a; return (_a = r.users) === null || _a === void 0 ? void 0 : _a.includes(ownerId); }).map((r) => r.name));
            const session = Session_1.default.get(`${ownerType}/${ownerId}`);
            await session.withPermissions();
            roles.push(...session.permissions);
        }
        else if (ownerType === 'device') {
            roles.push('authenticated.device');
            roles.push(...Config_1.default.rbac.roles.filter((r) => { var _a; return (_a = r.devices) === null || _a === void 0 ? void 0 : _a.includes(ownerId); }).map((r) => r.name));
            const session = Session_1.default.get(`${ownerType}/${ownerId}`);
            await session.withPermissions();
            roles.push(...session.permissions);
        }
        else if (ownerType === 'app') {
            roles.push('authenticated.app');
            roles.push(`app.${ownerId}`);
        }
        return [...new Set(roles)];
    },
    permissions: Config_1.default.rbac.permissions,
});
