feat(rbac): role-based access control module (#14310)
This commit is contained in:
committed by
GitHub
parent
d6d7d14a6a
commit
1bfde8dc57
6
packages/modules/rbac/.gitignore
vendored
Normal file
6
packages/modules/rbac/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/dist
|
||||
node_modules
|
||||
.DS_store
|
||||
.env*
|
||||
.env
|
||||
*.sql
|
||||
10
packages/modules/rbac/jest.config.js
Normal file
10
packages/modules/rbac/jest.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const defineJestConfig = require("../../../define_jest_config")
|
||||
module.exports = defineJestConfig({
|
||||
moduleNameMapper: {
|
||||
"^@models": "<rootDir>/src/models",
|
||||
"^@services": "<rootDir>/src/services",
|
||||
"^@repositories": "<rootDir>/src/repositories",
|
||||
"^@types": "<rootDir>/src/types",
|
||||
"^@utils": "<rootDir>/src/utils",
|
||||
},
|
||||
})
|
||||
6
packages/modules/rbac/mikro-orm.config.dev.ts
Normal file
6
packages/modules/rbac/mikro-orm.config.dev.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { defineMikroOrmCliConfig } from "@medusajs/framework/utils"
|
||||
import * as entities from "./src/models"
|
||||
|
||||
export default defineMikroOrmCliConfig("rbac", {
|
||||
entities: Object.values(entities),
|
||||
})
|
||||
45
packages/modules/rbac/package.json
Normal file
45
packages/modules/rbac/package.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "@medusajs/rbac",
|
||||
"version": "2.12.4",
|
||||
"description": "Medusa RBAC module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"!dist/**/__tests__",
|
||||
"!dist/**/__mocks__",
|
||||
"!dist/**/__fixtures__"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/medusajs/medusa",
|
||||
"directory": "packages/modules/rbac"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"author": "Medusa",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"watch": "yarn run -T tsc --build --watch",
|
||||
"watch:test": "yarn run -T tsc --build tsconfig.spec.json --watch",
|
||||
"resolve:aliases": "yarn run -T tsc --showConfig -p tsconfig.json > tsconfig.resolved.json && yarn run -T tsc-alias -p tsconfig.resolved.json && yarn run -T rimraf tsconfig.resolved.json",
|
||||
"build": "yarn run -T rimraf dist && yarn run -T tsc --build && npm run resolve:aliases",
|
||||
"test": "../../../node_modules/.bin/jest --passWithNoTests --bail --forceExit --testPathPattern=src",
|
||||
"test:integration": "../../../node_modules/.bin/jest --passWithNoTests --forceExit --testPathPattern=\"integration-tests/__tests__/.*\\.ts\"",
|
||||
"migration:initial": "MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts MIKRO_ORM_ALLOW_GLOBAL_CLI=true medusa-mikro-orm migration:create --initial",
|
||||
"migration:create": "MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts MIKRO_ORM_ALLOW_GLOBAL_CLI=true medusa-mikro-orm migration:create",
|
||||
"migration:up": "MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts MIKRO_ORM_ALLOW_GLOBAL_CLI=true medusa-mikro-orm migration:up",
|
||||
"orm:cache:clear": "MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts MIKRO_ORM_ALLOW_GLOBAL_CLI=true medusa-mikro-orm cache:clear"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@medusajs/framework": "2.12.4",
|
||||
"@medusajs/test-utils": "2.12.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@medusajs/framework": "2.12.4"
|
||||
}
|
||||
}
|
||||
6
packages/modules/rbac/src/index.ts
Normal file
6
packages/modules/rbac/src/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Module } from "@medusajs/framework/utils"
|
||||
import { RbacModuleService } from "@services"
|
||||
|
||||
export default Module("rbac", {
|
||||
service: RbacModuleService,
|
||||
})
|
||||
568
packages/modules/rbac/src/migrations/.snapshot-medusa-rbac.json
Normal file
568
packages/modules/rbac/src/migrations/.snapshot-medusa-rbac.json
Normal file
@@ -0,0 +1,568 @@
|
||||
{
|
||||
"namespaces": [
|
||||
"public"
|
||||
],
|
||||
"name": "public",
|
||||
"tables": [
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"resource": {
|
||||
"name": "resource",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"operation": {
|
||||
"name": "operation",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"metadata": {
|
||||
"name": "metadata",
|
||||
"type": "jsonb",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "json"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"deleted_at": {
|
||||
"name": "deleted_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"length": 6,
|
||||
"mappedType": "datetime"
|
||||
}
|
||||
},
|
||||
"name": "rbac_policy",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"keyName": "IDX_rbac_policy_deleted_at",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_rbac_policy_deleted_at\" ON \"rbac_policy\" (\"deleted_at\") WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_rbac_policy_key_unique",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_rbac_policy_key_unique\" ON \"rbac_policy\" (\"key\") WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_rbac_policy_resource",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_rbac_policy_resource\" ON \"rbac_policy\" (\"resource\") WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_rbac_policy_operation",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_rbac_policy_operation\" ON \"rbac_policy\" (\"operation\") WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "rbac_policy_pkey",
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"composite": false,
|
||||
"constraint": true,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {},
|
||||
"nativeEnums": {}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"metadata": {
|
||||
"name": "metadata",
|
||||
"type": "jsonb",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "json"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"deleted_at": {
|
||||
"name": "deleted_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"length": 6,
|
||||
"mappedType": "datetime"
|
||||
}
|
||||
},
|
||||
"name": "rbac_role",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"keyName": "IDX_rbac_role_deleted_at",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_rbac_role_deleted_at\" ON \"rbac_role\" (\"deleted_at\") WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_rbac_role_name_unique",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_rbac_role_name_unique\" ON \"rbac_role\" (\"name\") WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "rbac_role_pkey",
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"composite": false,
|
||||
"constraint": true,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {},
|
||||
"nativeEnums": {}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"role_id": {
|
||||
"name": "role_id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"inherited_role_id": {
|
||||
"name": "inherited_role_id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"metadata": {
|
||||
"name": "metadata",
|
||||
"type": "jsonb",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "json"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"deleted_at": {
|
||||
"name": "deleted_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"length": 6,
|
||||
"mappedType": "datetime"
|
||||
}
|
||||
},
|
||||
"name": "rbac_role_inheritance",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"keyName": "IDX_rbac_role_inheritance_role_id",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_rbac_role_inheritance_role_id\" ON \"rbac_role_inheritance\" (\"role_id\") WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_rbac_role_inheritance_inherited_role_id",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_rbac_role_inheritance_inherited_role_id\" ON \"rbac_role_inheritance\" (\"inherited_role_id\") WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_rbac_role_inheritance_deleted_at",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_rbac_role_inheritance_deleted_at\" ON \"rbac_role_inheritance\" (\"deleted_at\") WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_rbac_role_inheritance_role_id_inherited_role_id_unique",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_rbac_role_inheritance_role_id_inherited_role_id_unique\" ON \"rbac_role_inheritance\" (\"role_id\", \"inherited_role_id\") WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "rbac_role_inheritance_pkey",
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"composite": false,
|
||||
"constraint": true,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {
|
||||
"rbac_role_inheritance_role_id_foreign": {
|
||||
"constraintName": "rbac_role_inheritance_role_id_foreign",
|
||||
"columnNames": [
|
||||
"role_id"
|
||||
],
|
||||
"localTableName": "public.rbac_role_inheritance",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.rbac_role",
|
||||
"updateRule": "cascade"
|
||||
},
|
||||
"rbac_role_inheritance_inherited_role_id_foreign": {
|
||||
"constraintName": "rbac_role_inheritance_inherited_role_id_foreign",
|
||||
"columnNames": [
|
||||
"inherited_role_id"
|
||||
],
|
||||
"localTableName": "public.rbac_role_inheritance",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.rbac_role",
|
||||
"updateRule": "cascade"
|
||||
}
|
||||
},
|
||||
"nativeEnums": {}
|
||||
},
|
||||
{
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"role_id": {
|
||||
"name": "role_id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"scope_id": {
|
||||
"name": "scope_id",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"metadata": {
|
||||
"name": "metadata",
|
||||
"type": "jsonb",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"mappedType": "json"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"length": 6,
|
||||
"default": "now()",
|
||||
"mappedType": "datetime"
|
||||
},
|
||||
"deleted_at": {
|
||||
"name": "deleted_at",
|
||||
"type": "timestamptz",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": true,
|
||||
"length": 6,
|
||||
"mappedType": "datetime"
|
||||
}
|
||||
},
|
||||
"name": "rbac_role_policy",
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"keyName": "IDX_rbac_role_policy_role_id",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_rbac_role_policy_role_id\" ON \"rbac_role_policy\" (\"role_id\") WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_rbac_role_policy_scope_id",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_rbac_role_policy_scope_id\" ON \"rbac_role_policy\" (\"scope_id\") WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_rbac_role_policy_deleted_at",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_rbac_role_policy_deleted_at\" ON \"rbac_role_policy\" (\"deleted_at\") WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "IDX_rbac_role_policy_role_id_scope_id_unique",
|
||||
"columnNames": [],
|
||||
"composite": false,
|
||||
"constraint": false,
|
||||
"primary": false,
|
||||
"unique": false,
|
||||
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_rbac_role_policy_role_id_scope_id_unique\" ON \"rbac_role_policy\" (\"role_id\", \"scope_id\") WHERE deleted_at IS NULL"
|
||||
},
|
||||
{
|
||||
"keyName": "rbac_role_policy_pkey",
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"composite": false,
|
||||
"constraint": true,
|
||||
"primary": true,
|
||||
"unique": true
|
||||
}
|
||||
],
|
||||
"checks": [],
|
||||
"foreignKeys": {
|
||||
"rbac_role_policy_role_id_foreign": {
|
||||
"constraintName": "rbac_role_policy_role_id_foreign",
|
||||
"columnNames": [
|
||||
"role_id"
|
||||
],
|
||||
"localTableName": "public.rbac_role_policy",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.rbac_role",
|
||||
"updateRule": "cascade"
|
||||
},
|
||||
"rbac_role_policy_scope_id_foreign": {
|
||||
"constraintName": "rbac_role_policy_scope_id_foreign",
|
||||
"columnNames": [
|
||||
"scope_id"
|
||||
],
|
||||
"localTableName": "public.rbac_role_policy",
|
||||
"referencedColumnNames": [
|
||||
"id"
|
||||
],
|
||||
"referencedTableName": "public.rbac_policy",
|
||||
"updateRule": "cascade"
|
||||
}
|
||||
},
|
||||
"nativeEnums": {}
|
||||
}
|
||||
],
|
||||
"nativeEnums": {}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { Migration } from '@mikro-orm/migrations';
|
||||
|
||||
export class Migration20251215113723 extends Migration {
|
||||
|
||||
override async up(): Promise<void> {
|
||||
this.addSql(`alter table if exists "rbac_role_policy" drop constraint if exists "rbac_role_policy_role_id_scope_id_unique";`);
|
||||
this.addSql(`alter table if exists "rbac_role_inheritance" drop constraint if exists "rbac_role_inheritance_role_id_inherited_role_id_unique";`);
|
||||
this.addSql(`alter table if exists "rbac_role" drop constraint if exists "rbac_role_name_unique";`);
|
||||
this.addSql(`alter table if exists "rbac_policy" drop constraint if exists "rbac_policy_key_unique";`);
|
||||
this.addSql(`create table if not exists "rbac_policy" ("id" text not null, "key" text not null, "resource" text not null, "operation" text not null, "name" text null, "description" text null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "rbac_policy_pkey" primary key ("id"));`);
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_rbac_policy_deleted_at" ON "rbac_policy" ("deleted_at") WHERE deleted_at IS NULL;`);
|
||||
this.addSql(`CREATE UNIQUE INDEX IF NOT EXISTS "IDX_rbac_policy_key_unique" ON "rbac_policy" ("key") WHERE deleted_at IS NULL;`);
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_rbac_policy_resource" ON "rbac_policy" ("resource") WHERE deleted_at IS NULL;`);
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_rbac_policy_operation" ON "rbac_policy" ("operation") WHERE deleted_at IS NULL;`);
|
||||
|
||||
this.addSql(`create table if not exists "rbac_role" ("id" text not null, "name" text not null, "description" text null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "rbac_role_pkey" primary key ("id"));`);
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_rbac_role_deleted_at" ON "rbac_role" ("deleted_at") WHERE deleted_at IS NULL;`);
|
||||
this.addSql(`CREATE UNIQUE INDEX IF NOT EXISTS "IDX_rbac_role_name_unique" ON "rbac_role" ("name") WHERE deleted_at IS NULL;`);
|
||||
|
||||
this.addSql(`create table if not exists "rbac_role_inheritance" ("id" text not null, "role_id" text not null, "inherited_role_id" text not null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "rbac_role_inheritance_pkey" primary key ("id"));`);
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_rbac_role_inheritance_role_id" ON "rbac_role_inheritance" ("role_id") WHERE deleted_at IS NULL;`);
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_rbac_role_inheritance_inherited_role_id" ON "rbac_role_inheritance" ("inherited_role_id") WHERE deleted_at IS NULL;`);
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_rbac_role_inheritance_deleted_at" ON "rbac_role_inheritance" ("deleted_at") WHERE deleted_at IS NULL;`);
|
||||
this.addSql(`CREATE UNIQUE INDEX IF NOT EXISTS "IDX_rbac_role_inheritance_role_id_inherited_role_id_unique" ON "rbac_role_inheritance" ("role_id", "inherited_role_id") WHERE deleted_at IS NULL;`);
|
||||
|
||||
this.addSql(`create table if not exists "rbac_role_policy" ("id" text not null, "role_id" text not null, "scope_id" text not null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "rbac_role_policy_pkey" primary key ("id"));`);
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_rbac_role_policy_role_id" ON "rbac_role_policy" ("role_id") WHERE deleted_at IS NULL;`);
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_rbac_role_policy_scope_id" ON "rbac_role_policy" ("scope_id") WHERE deleted_at IS NULL;`);
|
||||
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_rbac_role_policy_deleted_at" ON "rbac_role_policy" ("deleted_at") WHERE deleted_at IS NULL;`);
|
||||
this.addSql(`CREATE UNIQUE INDEX IF NOT EXISTS "IDX_rbac_role_policy_role_id_scope_id_unique" ON "rbac_role_policy" ("role_id", "scope_id") WHERE deleted_at IS NULL;`);
|
||||
|
||||
this.addSql(`alter table if exists "rbac_role_inheritance" add constraint "rbac_role_inheritance_role_id_foreign" foreign key ("role_id") references "rbac_role" ("id") on update cascade;`);
|
||||
this.addSql(`alter table if exists "rbac_role_inheritance" add constraint "rbac_role_inheritance_inherited_role_id_foreign" foreign key ("inherited_role_id") references "rbac_role" ("id") on update cascade;`);
|
||||
|
||||
this.addSql(`alter table if exists "rbac_role_policy" add constraint "rbac_role_policy_role_id_foreign" foreign key ("role_id") references "rbac_role" ("id") on update cascade;`);
|
||||
this.addSql(`alter table if exists "rbac_role_policy" add constraint "rbac_role_policy_scope_id_foreign" foreign key ("scope_id") references "rbac_policy" ("id") on update cascade;`);
|
||||
}
|
||||
|
||||
}
|
||||
4
packages/modules/rbac/src/models/index.ts
Normal file
4
packages/modules/rbac/src/models/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { default as RbacPolicy } from "./rbac-policy"
|
||||
export { default as RbacRole } from "./rbac-role"
|
||||
export { default as RbacRoleInheritance } from "./rbac-role-inheritance"
|
||||
export { default as RbacRolePolicy } from "./rbac-role-policy"
|
||||
29
packages/modules/rbac/src/models/rbac-policy.ts
Normal file
29
packages/modules/rbac/src/models/rbac-policy.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { model } from "@medusajs/framework/utils"
|
||||
|
||||
const RbacPolicy = model
|
||||
.define("rbac_policy", {
|
||||
id: model.id({ prefix: "rpol" }).primaryKey(),
|
||||
key: model.text().searchable(),
|
||||
resource: model.text().searchable(),
|
||||
operation: model.text().searchable(),
|
||||
name: model.text().searchable().nullable(),
|
||||
description: model.text().nullable(),
|
||||
metadata: model.json().nullable(),
|
||||
})
|
||||
.indexes([
|
||||
{
|
||||
on: ["key"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
{
|
||||
on: ["resource"],
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
{
|
||||
on: ["operation"],
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
])
|
||||
|
||||
export default RbacPolicy
|
||||
29
packages/modules/rbac/src/models/rbac-role-inheritance.ts
Normal file
29
packages/modules/rbac/src/models/rbac-role-inheritance.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { model } from "@medusajs/framework/utils"
|
||||
import RbacRole from "./rbac-role"
|
||||
|
||||
const RbacRoleInheritance = model
|
||||
.define("rbac_role_inheritance", {
|
||||
id: model.id({ prefix: "rlin" }).primaryKey(),
|
||||
role: model.belongsTo(() => RbacRole, { mappedBy: "inherited_roles" }),
|
||||
inherited_role: model.belongsTo(() => RbacRole, {
|
||||
mappedBy: "inheritedBy",
|
||||
}),
|
||||
metadata: model.json().nullable(),
|
||||
})
|
||||
.indexes([
|
||||
{
|
||||
on: ["role_id"],
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
{
|
||||
on: ["inherited_role_id"],
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
{
|
||||
on: ["role_id", "inherited_role_id"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
])
|
||||
|
||||
export default RbacRoleInheritance
|
||||
28
packages/modules/rbac/src/models/rbac-role-policy.ts
Normal file
28
packages/modules/rbac/src/models/rbac-role-policy.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { model } from "@medusajs/framework/utils"
|
||||
import RbacPolicy from "./rbac-policy"
|
||||
import RbacRole from "./rbac-role"
|
||||
|
||||
const RbacRolePolicy = model
|
||||
.define("rbac_role_policy", {
|
||||
id: model.id({ prefix: "rlpl" }).primaryKey(),
|
||||
role: model.belongsTo(() => RbacRole),
|
||||
scope: model.belongsTo(() => RbacPolicy),
|
||||
metadata: model.json().nullable(),
|
||||
})
|
||||
.indexes([
|
||||
{
|
||||
on: ["role_id"],
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
{
|
||||
on: ["scope_id"],
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
{
|
||||
on: ["role_id", "scope_id"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
])
|
||||
|
||||
export default RbacRolePolicy
|
||||
18
packages/modules/rbac/src/models/rbac-role.ts
Normal file
18
packages/modules/rbac/src/models/rbac-role.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { model } from "@medusajs/framework/utils"
|
||||
|
||||
const RbacRole = model
|
||||
.define("rbac_role", {
|
||||
id: model.id({ prefix: "role" }).primaryKey(),
|
||||
name: model.text().searchable(),
|
||||
description: model.text().nullable(),
|
||||
metadata: model.json().nullable(),
|
||||
})
|
||||
.indexes([
|
||||
{
|
||||
on: ["name"],
|
||||
unique: true,
|
||||
where: "deleted_at IS NULL",
|
||||
},
|
||||
])
|
||||
|
||||
export default RbacRole
|
||||
1
packages/modules/rbac/src/repositories/index.ts
Normal file
1
packages/modules/rbac/src/repositories/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./rbac"
|
||||
88
packages/modules/rbac/src/repositories/rbac.ts
Normal file
88
packages/modules/rbac/src/repositories/rbac.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { SqlEntityManager } from "@medusajs/framework/mikro-orm/postgresql"
|
||||
import { Context } from "@medusajs/framework/types"
|
||||
import { MikroOrmBase } from "@medusajs/framework/utils"
|
||||
|
||||
export class RbacRepository extends MikroOrmBase {
|
||||
constructor() {
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
super(...arguments)
|
||||
}
|
||||
|
||||
async listPoliciesForRole(
|
||||
roleId: string,
|
||||
sharedContext: Context = {}
|
||||
): Promise<any[]> {
|
||||
const policiesByRole = await this.listPoliciesForRoles(
|
||||
[roleId],
|
||||
sharedContext
|
||||
)
|
||||
return policiesByRole.get(roleId) || []
|
||||
}
|
||||
|
||||
async listPoliciesForRoles(
|
||||
roleIds: string[],
|
||||
sharedContext: Context = {}
|
||||
): Promise<Map<string, any[]>> {
|
||||
const manager = this.getActiveManager<SqlEntityManager>(sharedContext)
|
||||
const knex = manager.getKnex()
|
||||
|
||||
if (!roleIds?.length) {
|
||||
return new Map()
|
||||
}
|
||||
|
||||
const placeholders = roleIds.map(() => "?").join(",")
|
||||
|
||||
const query = `
|
||||
WITH RECURSIVE role_hierarchy AS (
|
||||
SELECT id, name, id as original_role_id
|
||||
FROM rbac_role
|
||||
WHERE id IN (${placeholders}) AND deleted_at IS NULL
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT r.id, r.name, rh.original_role_id
|
||||
FROM rbac_role r
|
||||
INNER JOIN rbac_role_inheritance ri ON ri.inherited_role_id = r.id
|
||||
INNER JOIN role_hierarchy rh ON rh.id = ri.role_id
|
||||
WHERE r.deleted_at IS NULL AND ri.deleted_at IS NULL
|
||||
)
|
||||
SELECT DISTINCT
|
||||
rh.original_role_id,
|
||||
p.id,
|
||||
p.key,
|
||||
p.resource,
|
||||
p.operation,
|
||||
p.name,
|
||||
p.description,
|
||||
p.metadata,
|
||||
p.created_at,
|
||||
p.updated_at,
|
||||
CASE WHEN rp.role_id = rh.original_role_id THEN NULL ELSE rp.role_id END as inherited_from_role_id
|
||||
FROM rbac_policy p
|
||||
INNER JOIN rbac_role_policy rp ON rp.scope_id = p.id
|
||||
INNER JOIN role_hierarchy rh ON rh.id = rp.role_id
|
||||
WHERE p.deleted_at IS NULL AND rp.deleted_at IS NULL
|
||||
ORDER BY rh.original_role_id, p.resource, p.operation, p.key
|
||||
`
|
||||
|
||||
const result = await knex.raw(query, roleIds)
|
||||
const rows = result.rows || []
|
||||
|
||||
// Group policies by role_id
|
||||
const policiesByRole = new Map<string, any[]>()
|
||||
|
||||
for (const row of rows) {
|
||||
const roleId = row.original_role_id
|
||||
delete row.original_role_id
|
||||
|
||||
if (!policiesByRole.has(roleId)) {
|
||||
policiesByRole.set(roleId, [])
|
||||
}
|
||||
|
||||
policiesByRole.get(roleId)!.push(row)
|
||||
}
|
||||
|
||||
return policiesByRole
|
||||
}
|
||||
}
|
||||
1
packages/modules/rbac/src/services/index.ts
Normal file
1
packages/modules/rbac/src/services/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as RbacModuleService } from "./rbac-module-service"
|
||||
105
packages/modules/rbac/src/services/rbac-module-service.ts
Normal file
105
packages/modules/rbac/src/services/rbac-module-service.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { Context, FindConfig } from "@medusajs/framework/types"
|
||||
import {
|
||||
InjectManager,
|
||||
MedusaContext,
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import {
|
||||
RbacPolicy,
|
||||
RbacRole,
|
||||
RbacRoleInheritance,
|
||||
RbacRolePolicy,
|
||||
} from "@models"
|
||||
import { RbacRepository } from "../repositories"
|
||||
|
||||
type InjectedDependencies = {
|
||||
rbacRepository: RbacRepository
|
||||
}
|
||||
|
||||
export default class RbacModuleService extends MedusaService<{
|
||||
RbacRole: { dto: any }
|
||||
RbacPolicy: { dto: any }
|
||||
RbacRoleInheritance: { dto: any }
|
||||
RbacRolePolicy: { dto: any }
|
||||
}>({
|
||||
RbacRole,
|
||||
RbacPolicy,
|
||||
RbacRoleInheritance,
|
||||
RbacRolePolicy,
|
||||
}) {
|
||||
protected readonly rbacRepository_: RbacRepository
|
||||
|
||||
constructor({ rbacRepository }: InjectedDependencies) {
|
||||
// @ts-ignore
|
||||
super(...arguments)
|
||||
this.rbacRepository_ = rbacRepository
|
||||
}
|
||||
|
||||
@InjectManager()
|
||||
async listPoliciesForRole(
|
||||
roleId: string,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<any[]> {
|
||||
return await this.rbacRepository_.listPoliciesForRole(roleId, sharedContext)
|
||||
}
|
||||
|
||||
@InjectManager()
|
||||
// @ts-expect-error
|
||||
async listRbacRoles(
|
||||
filters: any = {},
|
||||
config: FindConfig<any> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<any[]> {
|
||||
const roles = await super.listRbacRoles(filters, config, sharedContext)
|
||||
|
||||
const shouldIncludePolicies =
|
||||
config.relations?.includes("policies") ||
|
||||
config.select?.includes("policies")
|
||||
|
||||
if (shouldIncludePolicies && roles.length > 0) {
|
||||
const roleIds = roles.map((role) => role.id)
|
||||
const policiesByRole = await this.rbacRepository_.listPoliciesForRoles(
|
||||
roleIds,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
for (const role of roles) {
|
||||
role.policies = policiesByRole.get(role.id) || []
|
||||
}
|
||||
}
|
||||
|
||||
return roles
|
||||
}
|
||||
|
||||
@InjectManager()
|
||||
// @ts-expect-error
|
||||
async listAndCountRbacRoles(
|
||||
filters: any = {},
|
||||
config: FindConfig<any> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<[any[], number]> {
|
||||
const [roles, count] = await super.listAndCountRbacRoles(
|
||||
filters,
|
||||
config,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const shouldIncludePolicies =
|
||||
config.relations?.includes("policies") ||
|
||||
config.select?.includes("policies")
|
||||
|
||||
if (shouldIncludePolicies && roles.length > 0) {
|
||||
const roleIds = roles.map((role) => role.id)
|
||||
const policiesByRole = await this.rbacRepository_.listPoliciesForRoles(
|
||||
roleIds,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
for (const role of roles) {
|
||||
role.policies = policiesByRole.get(role.id) || []
|
||||
}
|
||||
}
|
||||
|
||||
return [roles, count]
|
||||
}
|
||||
}
|
||||
1
packages/modules/rbac/src/types/index.ts
Normal file
1
packages/modules/rbac/src/types/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type RbacModuleOptions = Record<string, unknown>
|
||||
12
packages/modules/rbac/tsconfig.json
Normal file
12
packages/modules/rbac/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../../_tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@models": ["./src/models"],
|
||||
"@services": ["./src/services"],
|
||||
"@repositories": ["./src/repositories"],
|
||||
"@types": ["./src/types"],
|
||||
"@utils": ["./src/utils"]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user