From 6f41bc2b3040380c6b9a089bc191c669b3b2ac05 Mon Sep 17 00:00:00 2001 From: "Xc@centOs" Date: Fri, 28 Apr 2023 20:02:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E6=96=B0=E7=9A=84auth=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E7=9A=84=E5=A4=A7=E9=87=8F=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/AppLoader.js | 6 ++++-- lib/DbStore.d.ts | 9 ++++++-- lib/DbStore.js | 40 ++++++++++++++++++++++++++++++++++- package.json | 2 +- src/AppLoader.ts | 38 +++++++++++++++++++++++++++++---- src/DbStore.ts | 55 +++++++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 137 insertions(+), 13 deletions(-) diff --git a/lib/AppLoader.js b/lib/AppLoader.js index 4fe2e07..11873d6 100644 --- a/lib/AppLoader.js +++ b/lib/AppLoader.js @@ -52,9 +52,10 @@ class AppLoader extends types_1.AppLoader { constructor(path, contextBuilder, dbConfig) { super(path); const { storageSchema } = require(`${path}/lib/oak-app-domain/Storage`); + const { ActionCascadePathGraph, RelationCascadePathGraph } = require(`${path}/lib/oak-app-domain/Relation`); this.externalDependencies = require(`${path}/lib/config/externalDependencies`).default; this.aspectDict = Object.assign({}, index_1.default, this.requireSth('lib/aspects/index')); - this.dbStore = new DbStore_1.DbStore(storageSchema, contextBuilder, dbConfig); + this.dbStore = new DbStore_1.DbStore(storageSchema, contextBuilder, dbConfig, ActionCascadePathGraph, RelationCascadePathGraph); this.contextBuilder = contextBuilder; } initTriggers() { @@ -67,7 +68,8 @@ class AppLoader extends types_1.AppLoader { adTriggers.forEach((trigger) => this.dbStore.registerTrigger(trigger)); checkers.forEach((checker) => this.dbStore.registerChecker(checker)); adCheckers.forEach((checker) => this.dbStore.registerChecker(checker)); - const dynamicCheckers = (0, checkers_1.createDynamicCheckers)(this.dbStore.getSchema(), authDict); + // todo cascadeRemoveTrigger要挪到Schema定义里 + const dynamicCheckers = (0, checkers_1.createDynamicCheckers)(this.dbStore.getSchema(), {}); dynamicCheckers.forEach((checker) => this.dbStore.registerChecker(checker)); const dynamicTriggers = (0, triggers_1.createDynamicTriggers)(this.dbStore.getSchema()); dynamicTriggers.forEach((trigger) => this.dbStore.registerTrigger(trigger)); diff --git a/lib/DbStore.d.ts b/lib/DbStore.d.ts index cb6bd06..d172c8b 100644 --- a/lib/DbStore.d.ts +++ b/lib/DbStore.d.ts @@ -1,11 +1,16 @@ import { MysqlStore, MySqlSelectOption, MysqlOperateOption } from 'oak-db'; -import { EntityDict, StorageSchema, Trigger, Checker, SelectOption } from 'oak-domain/lib/types'; +import { EntityDict, StorageSchema, Trigger, Checker, SelectOption, AuthCascadePath } from 'oak-domain/lib/types'; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; import { MySQLConfiguration } from 'oak-db/lib/MySQL/types/Configuration'; import { AsyncContext, AsyncRowStore } from 'oak-domain/lib/store/AsyncRowStore'; export declare class DbStore> extends MysqlStore implements AsyncRowStore { private executor; - constructor(storageSchema: StorageSchema, contextBuilder: (scene?: string) => (store: DbStore) => Promise>, mysqlConfiguration: MySQLConfiguration); + private relationAuth; + constructor(storageSchema: StorageSchema, contextBuilder: (scene?: string) => (store: DbStore) => Promise, mysqlConfiguration: MySQLConfiguration, actionCascadeGraph: AuthCascadePath[], relationCascadeGraph: AuthCascadePath[]); + /** + * relationAuth中需要缓存一些维表的数据 + */ + private initRelationAuthTriggers; protected cascadeUpdateAsync(entity: T, operation: ED[T]['Operation'], context: AsyncContext, option: MysqlOperateOption): Promise>; operate(entity: T, operation: ED[T]['Operation'], context: Cxt, option: MysqlOperateOption): Promise>; select(entity: T, selection: ED[T]['Selection'], context: Cxt, option: MySqlSelectOption): Promise[]>; diff --git a/lib/DbStore.js b/lib/DbStore.js index 5cc275e..b449228 100644 --- a/lib/DbStore.js +++ b/lib/DbStore.js @@ -3,16 +3,52 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.DbStore = void 0; const oak_db_1 = require("oak-db"); const TriggerExecutor_1 = require("oak-domain/lib/store/TriggerExecutor"); +const RelationAuth_1 = require("oak-domain/lib/store/RelationAuth"); class DbStore extends oak_db_1.MysqlStore { executor; - constructor(storageSchema, contextBuilder, mysqlConfiguration) { + relationAuth; + constructor(storageSchema, contextBuilder, mysqlConfiguration, actionCascadeGraph, relationCascadeGraph) { super(storageSchema, mysqlConfiguration); this.executor = new TriggerExecutor_1.TriggerExecutor((scene) => contextBuilder(scene)(this)); + this.relationAuth = new RelationAuth_1.RelationAuth(actionCascadeGraph, relationCascadeGraph, storageSchema); + this.initRelationAuthTriggers(contextBuilder); + } + /** + * relationAuth中需要缓存一些维表的数据 + */ + async initRelationAuthTriggers(contextBuilder) { + const context = await contextBuilder()(this); + const freeActionAuths = await this.select('freeActionAuth', { + data: { + id: 1, + deActions: 1, + destEntity: 1, + }, + }, context, { + dontCollect: true, + }); + this.relationAuth.setFreeActionAuths(freeActionAuths); + const directActionAuths = await this.select('directActionAuth', { + data: { + id: 1, + rootEntity: 1, + path: 1, + deActions: 1, + destEntity: 1, + }, + }, context, { + dontCollect: true, + }); + this.relationAuth.setDirectionActionAuths(directActionAuths); + await context.commit(); + const triggers = this.relationAuth.getAuthDataTriggers(); + triggers.forEach((trigger) => this.registerTrigger(trigger)); } async cascadeUpdateAsync(entity, operation, context, option) { if (!option.blockTrigger) { await this.executor.preOperation(entity, operation, context, option); } + await this.relationAuth.checkRelationAsync(entity, operation, context); const result = await super.cascadeUpdateAsync(entity, operation, context, option); if (!option.blockTrigger) { await this.executor.postOperation(entity, operation, context, option); @@ -47,6 +83,7 @@ class DbStore extends oak_db_1.MysqlStore { Object.assign(selection, { action: 'select', }); + await this.relationAuth.checkRelationAsync(entity, selection, context); if (!option.blockTrigger) { await this.executor.preOperation(entity, selection, context, option); } @@ -75,6 +112,7 @@ class DbStore extends oak_db_1.MysqlStore { const selection2 = Object.assign({ action: 'select', }, selection); + await this.relationAuth.checkRelationAsync(entity, selection2, context); if (!option.blockTrigger) { await this.executor.preOperation(entity, selection2, context, option); } diff --git a/package.json b/package.json index f2f9f19..e1fbb5d 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,6 @@ "copyfiles": "^2.4.1", "ts-node": "~10.9.1", "tslib": "^2.4.0", - "typescript": "~4.7.4" + "typescript": "^4.7.4" } } diff --git a/src/AppLoader.ts b/src/AppLoader.ts index cc74d3c..dee2ac3 100644 --- a/src/AppLoader.ts +++ b/src/AppLoader.ts @@ -45,19 +45,47 @@ export class AppLoader = {}; sthExternal.forEach( (sth2, idx) => { assert(typeof sth2 === 'object' && !(sth2 instanceof Array), `${join(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象不是非数组对象,与项目对应路径的输出不一致`); const inter = intersection(Object.keys(sthOut), Object.keys(sth2)); if (inter.length > 0) { console.warn(`${join(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象中的key值【${inter.join(',')}】与其它对应路径输出的key值有冲突,请仔细检查避免错误`); + inter.forEach( + (ele) => { + if (sth2[ele] instanceof Array && sthOut[ele]) { + assert(sthOut[ele] instanceof Array, `${join(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象的${ele}键值是数组,但之前的相应对象的${ele}却不是,请仔细检查以避免错误`); + console.warn(`${join(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象中的key值【${ele}】与其它对应路径输出的key值【${ele}】将以数组格式进行合并,请仔细检查避免错误`); + sth2[ele].push(...sthOut[ele]); + } + else if (!(sth2[ele] instanceof Array) && sthOut[ele]) { + assert(!(sthOut[ele] instanceof Array), `${join(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象的${ele}键值不是数组,但之前的相应对象的${ele}却是,请仔细检查以避免错误`); + console.warn(`${join(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象中的key值【${ele}】将对与其它对应路径输出的key值【${ele}】进行覆盖,请仔细检查避免错误`); + } + } + ) } Object.assign(sthOut, sth2); } ); + const inter = intersection(Object.keys(sthOut), Object.keys(sth)); - assert(inter.length === 0, `项目${filePath}中的default输出与第三方库中的输出在键值${inter.join(',')}上冲突,请处理`); + if (inter.length > 0) { + inter.forEach( + (ele) => { + if (sth[ele] instanceof Array && sthOut[ele]) { + assert(sthOut[ele] instanceof Array, `项目${filePath}中的default输出对象的${ele}键值是数组,但之前的相应对象的${ele}却不是,请仔细检查以避免错误`); + console.warn(`项目${filePath}中的default输出对象中的key值【${ele}】与其它引用包该路径输出的key值【${ele}】将以数组格式进行合并,请仔细检查避免错误`); + sth[ele].push(...sthOut[ele]); + } + else if (!(sth[ele] instanceof Array) && sthOut[ele]) { + assert(!(sthOut[ele] instanceof Array), `项目${filePath}中的default输出对象的${ele}键值不是数组,但之前的相应对象的${ele}却是,请仔细检查以避免错误`); + console.warn(`项目${filePath}中的default输出对象中的key值【${ele}】将对其它引用包该路径输出的key值【${ele}】进行覆盖,请仔细检查避免错误`); + } + } + ) + } Object.assign(sthOut, sth); return sthOut; } @@ -65,9 +93,10 @@ export class AppLoader (store: DbStore) => Promise, dbConfig: MySQLConfiguration) { super(path); const { storageSchema } = require(`${path}/lib/oak-app-domain/Storage`); + const { ActionCascadePathGraph, RelationCascadePathGraph } = require(`${path}/lib/oak-app-domain/Relation`); this.externalDependencies = require(`${path}/lib/config/externalDependencies`).default; this.aspectDict = Object.assign({}, generalAspectDict, this.requireSth('lib/aspects/index')); - this.dbStore = new DbStore(storageSchema, contextBuilder, dbConfig); + this.dbStore = new DbStore(storageSchema, contextBuilder, dbConfig, ActionCascadePathGraph, RelationCascadePathGraph); this.contextBuilder = contextBuilder; } @@ -91,7 +120,8 @@ export class AppLoader this.dbStore.registerChecker(checker) ); - const dynamicCheckers = createDynamicCheckers(this.dbStore.getSchema(), authDict); + // todo cascadeRemoveTrigger要挪到Schema定义里 + const dynamicCheckers = createDynamicCheckers(this.dbStore.getSchema(), {}); dynamicCheckers.forEach( (checker) => this.dbStore.registerChecker(checker) ); diff --git a/src/DbStore.ts b/src/DbStore.ts index 1489bd3..96444f9 100644 --- a/src/DbStore.ts +++ b/src/DbStore.ts @@ -1,24 +1,72 @@ import { MysqlStore, MySqlSelectOption, MysqlOperateOption } from 'oak-db'; -import { EntityDict, Context, StorageSchema, Trigger, Checker, RowStore, SelectOption } from 'oak-domain/lib/types'; +import { EntityDict, Context, StorageSchema, Trigger, Checker, RowStore, SelectOption, AuthCascadePath } from 'oak-domain/lib/types'; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain'; import { TriggerExecutor } from 'oak-domain/lib/store/TriggerExecutor'; import { MySQLConfiguration, } from 'oak-db/lib/MySQL/types/Configuration'; import { AsyncContext, AsyncRowStore } from 'oak-domain/lib/store/AsyncRowStore'; +import { RelationAuth } from 'oak-domain/lib/store/RelationAuth'; export class DbStore> extends MysqlStore implements AsyncRowStore { private executor: TriggerExecutor; + private relationAuth: RelationAuth; - constructor(storageSchema: StorageSchema, contextBuilder: (scene?: string) => (store: DbStore) => Promise>, mysqlConfiguration: MySQLConfiguration) { + constructor( + storageSchema: StorageSchema, + contextBuilder: (scene?: string) => (store: DbStore) => Promise, + mysqlConfiguration: MySQLConfiguration, + actionCascadeGraph: AuthCascadePath[], + relationCascadeGraph: AuthCascadePath[]) { super(storageSchema, mysqlConfiguration); this.executor = new TriggerExecutor((scene) => contextBuilder(scene)(this)); + this.relationAuth = new RelationAuth(actionCascadeGraph, relationCascadeGraph, storageSchema); + this.initRelationAuthTriggers(contextBuilder); } + /** + * relationAuth中需要缓存一些维表的数据 + */ + private async initRelationAuthTriggers(contextBuilder: (scene?: string) => (store: DbStore) => Promise) { + const context = await contextBuilder()(this); + await context.begin(); + + // 先direct后free,因为RelationAuth中会根据free判断是否完成 + const directActionAuths = await this.select('directActionAuth', { + data: { + id: 1, + rootEntity: 1, + path: 1, + deActions: 1, + destEntity: 1, + }, + }, context, { + dontCollect: true, + }); + this.relationAuth.setDirectionActionAuths(directActionAuths as ED['directActionAuth']['OpSchema'][]); + const freeActionAuths = await this.select('freeActionAuth', { + data: { + id: 1, + deActions: 1, + destEntity: 1, + }, + }, context, { + dontCollect: true, + }); + this.relationAuth.setFreeActionAuths(freeActionAuths as ED['freeActionAuth']['OpSchema'][]); + + await context.commit(); + + const triggers = this.relationAuth.getAuthDataTriggers(); + triggers.forEach( + (trigger) => this.registerTrigger(trigger) + ); + } protected async cascadeUpdateAsync(entity: T, operation: ED[T]['Operation'], context: AsyncContext, option: MysqlOperateOption) { if (!option.blockTrigger) { await this.executor.preOperation(entity, operation, context, option); } + await this.relationAuth.checkRelationAsync(entity, operation, context); const result = await super.cascadeUpdateAsync(entity, operation, context, option); if (!option.blockTrigger) { await this.executor.postOperation(entity, operation, context, option); @@ -66,7 +114,7 @@ export class DbStore