@rsc-labs/medusa-rbac
Version:
The RBAC functionality for MedusaJS
227 lines • 17.2 kB
JavaScript
;
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.LicenceStatus = void 0;
const crypto = __importStar(require("crypto"));
const utils_1 = require("@medusajs/framework/utils");
const rbac_role_1 = __importDefault(require("./models/rbac-role"));
const rbac_permission_1 = __importDefault(require("./models/rbac-permission"));
const rbac_policy_1 = __importDefault(require("./models/rbac-policy"));
const types_1 = require("./types");
const rbac_permission_category_1 = __importDefault(require("./models/rbac-permission-category"));
var LicenceStatus;
(function (LicenceStatus) {
LicenceStatus["EXPIRED"] = "EXPIRED";
LicenceStatus["VALID"] = "VALID";
LicenceStatus["INVALID"] = "INVALID";
})(LicenceStatus || (exports.LicenceStatus = LicenceStatus = {}));
class RbacModuleService extends (0, utils_1.MedusaService)({
RbacRole: rbac_role_1.default,
RbacPolicy: rbac_policy_1.default,
RbacPermission: rbac_permission_1.default,
RbacPermissionCategory: rbac_permission_category_1.default
}) {
constructor({ logger }, options) {
super(...arguments);
this.logger_ = logger;
this.options_ = options || {
licenceKey: "",
};
this.KEYGEN_PRODUCT = "97aedaeb-1256-485a-aaa8-93766494c58f";
this.KEYGEN_PUBLIC_PEM =
`
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1AlHFWQ0PDSA+psXM+7M
Vt9pV9ItJEYJeroOw4yDluJm/WXgzjqkO+d/0aliglv0o6FPWl03qjS7VX9Y3Egt
NvoVc0hCttml2tG7trs/7xy3zatICXI5+8yg0BkxvUTtS2jiDi9AtGZ4nZOpkEJB
0arzGr06XBxE+C0XM7W1zIjZ8xGtP8eJMefwtwWF3e5mmQ5jtc1dPV5sS0zHZNgv
6UTYZvpuMnxWJlQ4gGzKs289xW904q1RN61S5LeACc3YCvCs5l0hBD2rXp1G+pBC
sDOeM2fAawgc6dD7dVUv4zm/zhRcHaPUaTUdqjB9FbHJbZZfyPvpPSnUjxsOcg8U
NQIDAQAB
-----END PUBLIC KEY-----
`;
}
async addRole(adminRbacRole) {
const newRole = await this.createRbacRoles({
name: adminRbacRole.name,
policies: []
});
if (newRole) {
const newPolicies = await this.createRbacPolicies(adminRbacRole.policies.map(policy => {
return {
permission: policy.permission.id,
type: policy.type,
role: newRole.id
};
}));
return {
...newRole,
policies: newPolicies
};
}
return undefined;
}
evaluatePolicy(policy, requestedType, matcher, actionType) {
if (policy.permission.matcherType === requestedType) {
if (requestedType === types_1.PermissionMatcherType.API) {
// It covers subroutes also
if (matcher.includes(policy.permission.matcher) && policy.permission.actionType === actionType) {
return policy.type;
}
}
else {
if (policy.permission.matcherType === requestedType && policy.permission.matcher === matcher && policy.permission.actionType === actionType) {
return policy.type;
}
}
}
return undefined;
}
async evaluateAuthorization(role, requestedType, matcher, actionType) {
const rbacRole = await this.retrieveRbacRole(role.id, {
relations: ["policies", "policies.permission"]
});
for (const configuredPolicy of rbacRole.policies) {
if (this.evaluatePolicy(configuredPolicy, requestedType, matcher, actionType) === types_1.PolicyType.DENY) {
return false;
}
}
return true;
}
async testAuthorization(role, requestedType, matcher) {
const rbacRole = await this.retrieveRbacRole(role.id, {
relations: ["policies", "policies.permission"]
});
const allowedActions = [];
const deniedActions = [];
for (const configuredPolicy of rbacRole.policies) {
for (const actionType of Object.values(types_1.ActionType)) {
if (this.evaluatePolicy(configuredPolicy, requestedType, matcher, actionType) === types_1.PolicyType.DENY) {
deniedActions.push(actionType);
}
else {
allowedActions.push(actionType);
}
}
}
return {
url: matcher,
denied: deniedActions,
allowed: allowedActions
};
}
async checkLicence() {
this.logger_.info('Checking licence');
function base64urlToBuffer(b64url) {
const b64 = b64url
.replace(/-/g, '+')
.replace(/_/g, '/')
.padEnd(Math.ceil(b64url.length / 4) * 4, '=');
return Buffer.from(b64, 'base64');
}
function decodeLicensePayload(licenseKey) {
const [payloadWithPrefix, signature] = licenseKey.split('.');
if (!payloadWithPrefix || !signature) {
throw new Error('Invalid license format (missing parts)');
}
const [prefix, encodedPayload] = payloadWithPrefix.split('/');
if (prefix !== 'key' || !encodedPayload) {
throw new Error(`Unsupported license prefix: ${prefix}`);
}
// Decode the encoded payload
const payloadBuffer = base64urlToBuffer(encodedPayload);
const payloadString = payloadBuffer.toString('utf-8');
let payload;
try {
payload = JSON.parse(payloadString);
}
catch (e) {
throw new Error(`Failed to parse license payload as JSON: ${payloadString}`);
}
return payload;
}
if (!this.options_.licenceKey) {
return LicenceStatus.INVALID;
}
try {
const payload = decodeLicensePayload(this.options_.licenceKey);
if (!payload.product) {
throw new Error('License payload does not contain an "product" field.');
}
if (payload.product !== this.KEYGEN_PRODUCT) {
throw new Error('Invalid product');
}
if (payload.expires) {
const expirationDate = new Date(payload.expires);
if (isNaN(expirationDate.getTime())) {
throw new Error(`Invalid expiration date: ${payload.expires}`);
}
// Compare with the current date.
if (expirationDate < new Date()) {
this.logger_.warn('❌ License has expired');
return LicenceStatus.EXPIRED;
}
this.logger_.debug(`License expiration date is valid:', ${expirationDate.toISOString()}`);
}
else {
this.logger_.debug(`Licence is lifetime`);
}
const [message, signatureB64] = this.options_.licenceKey.split('.');
if (!message || !signatureB64) {
throw new Error('Invalid license format');
}
const signatureBuffer = base64urlToBuffer(signatureB64);
const isValid = crypto.verify('sha256', Buffer.from(message), {
key: this.KEYGEN_PUBLIC_PEM,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: crypto.constants.RSA_PSS_SALTLEN_AUTO,
}, signatureBuffer);
if (!isValid) {
throw new Error('Signature does not match');
}
this.logger_.debug('✅ License is valid');
return LicenceStatus.VALID;
}
catch (error) {
this.logger_.error(`Licence is invalid: ${error.message}`);
return LicenceStatus.INVALID;
}
}
}
exports.default = RbacModuleService;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9tb2R1bGVzL3JiYWMvc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSwrQ0FBZ0M7QUFDaEMscURBQXlEO0FBQ3pELG1FQUF5QztBQUN6QywrRUFBcUQ7QUFDckQsdUVBQTZDO0FBQzdDLG1DQUE4SDtBQUM5SCxpR0FBc0U7QUFPdEUsSUFBWSxhQUlYO0FBSkQsV0FBWSxhQUFhO0lBQ3ZCLG9DQUFtQixDQUFBO0lBQ25CLGdDQUFlLENBQUE7SUFDZixvQ0FBbUIsQ0FBQTtBQUNyQixDQUFDLEVBSlcsYUFBYSw2QkFBYixhQUFhLFFBSXhCO0FBRUQsTUFBTSxpQkFBa0IsU0FBUSxJQUFBLHFCQUFhLEVBQUM7SUFDNUMsUUFBUSxFQUFSLG1CQUFRO0lBQ1IsVUFBVSxFQUFWLHFCQUFVO0lBQ1YsY0FBYyxFQUFkLHlCQUFjO0lBQ2Qsc0JBQXNCLEVBQXRCLGtDQUFzQjtDQUN2QixDQUFDO0lBT0EsWUFBWSxFQUFDLE1BQU0sRUFBQyxFQUFFLE9BQXVCO1FBQzNDLEtBQUssQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFBO1FBRW5CLElBQUksQ0FBQyxPQUFPLEdBQUcsTUFBTSxDQUFDO1FBQ3RCLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxJQUFJO1lBQ3pCLFVBQVUsRUFBRSxFQUFFO1NBQ2YsQ0FBQTtRQUVELElBQUksQ0FBQyxjQUFjLEdBQUcsc0NBQXNDLENBQUM7UUFDN0QsSUFBSSxDQUFDLGlCQUFpQjtZQUMxQjs7Ozs7Ozs7OztDQVVDLENBQUE7SUFDQyxDQUFDO0lBRUQsS0FBSyxDQUFDLE9BQU8sQ0FBQyxhQUF3QztRQUNwRCxNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxlQUFlLENBQUM7WUFDekMsSUFBSSxFQUFFLGFBQWEsQ0FBQyxJQUFJO1lBQ3hC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