From 1595dbb51a3f645091a7078da3f02ba6f05032e1 Mon Sep 17 00:00:00 2001 From: "Xc@centOs" Date: Thu, 19 Oct 2023 19:38:43 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86RelationAuth?= =?UTF-8?q?=E4=B8=ADupdateFree=E7=9A=84=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/base-app-domain/Relation.d.ts | 8 - lib/base-app-domain/Relation.js | 8 +- lib/compiler/schemalBuilder.d.ts | 16 ++ lib/compiler/schemalBuilder.js | 64 ++++++- lib/index.d.ts | 2 +- lib/index.js | 3 +- lib/store/RelationAuth.d.ts | 19 +- lib/store/RelationAuth.js | 259 +--------------------------- lib/types/Entity.d.ts | 4 +- src/checkers/index.ts | 2 +- src/compiler/schemalBuilder.ts | 131 +++++++++++++- src/index.ts | 2 +- src/store/RelationAuth.ts | 276 ++---------------------------- src/types/Entity.ts | 4 +- 14 files changed, 240 insertions(+), 558 deletions(-) diff --git a/lib/base-app-domain/Relation.d.ts b/lib/base-app-domain/Relation.d.ts index 5809730..b05da5f 100644 --- a/lib/base-app-domain/Relation.d.ts +++ b/lib/base-app-domain/Relation.d.ts @@ -1,10 +1,2 @@ -import { AuthCascadePath, AuthDeduceRelationMap } from "../types/Entity"; -import { EntityDict } from "./EntityDict"; import { CreateOperationData as Relation } from "./Relation/Schema"; -export declare const ActionCascadePathGraph: AuthCascadePath[]; -export declare const RelationCascadePathGraph: AuthCascadePath[]; export declare const relations: Relation[]; -export declare const deducedRelationMap: AuthDeduceRelationMap; -export declare const selectFreeEntities: (keyof EntityDict)[]; -export declare const updateFreeEntities: (keyof EntityDict)[]; -export declare const createFreeEntities: (keyof EntityDict)[]; diff --git a/lib/base-app-domain/Relation.js b/lib/base-app-domain/Relation.js index 5bea375..8358f35 100644 --- a/lib/base-app-domain/Relation.js +++ b/lib/base-app-domain/Relation.js @@ -1,10 +1,4 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.createFreeEntities = exports.updateFreeEntities = exports.selectFreeEntities = exports.deducedRelationMap = exports.relations = exports.RelationCascadePathGraph = exports.ActionCascadePathGraph = void 0; -exports.ActionCascadePathGraph = []; -exports.RelationCascadePathGraph = []; +exports.relations = void 0; exports.relations = []; -exports.deducedRelationMap = {}; -exports.selectFreeEntities = []; -exports.updateFreeEntities = []; -exports.createFreeEntities = []; diff --git a/lib/compiler/schemalBuilder.d.ts b/lib/compiler/schemalBuilder.d.ts index fcc60a1..65effd0 100644 --- a/lib/compiler/schemalBuilder.d.ts +++ b/lib/compiler/schemalBuilder.d.ts @@ -1,11 +1,27 @@ +/** + * 此函数不再使用 + * @param map + */ export declare function registerIgnoredForeignKeyMap(map: Record): void; +/** + * 此函数不再使用 + * @param map + */ export declare function registerFreeEntities(selectFreeEntities?: string[], createFreeEntities?: string[], updateFreeEntities?: string[]): void; +/** + * 此函数不再使用 + * @param map + */ export declare function registerIgnoredRelationPathMap(map: Record): void; /** * 很多路径虽然最后指向同一对象,但不能封掉,封了会导致查询的时候找不到对应的路径path * @param map */ export declare function registerFixedDestinationPathMap(map: Record): void; +/** + * 此函数不再使用 + * @param map + */ export declare function registerDeducedRelationMap(map: Record): void; export declare function analyzeEntities(inputDir: string, relativePath?: string): void; export declare function buildSchema(outputDir: string): void; diff --git a/lib/compiler/schemalBuilder.js b/lib/compiler/schemalBuilder.js index 1a0ee6a..8e1ad12 100644 --- a/lib/compiler/schemalBuilder.js +++ b/lib/compiler/schemalBuilder.js @@ -3255,6 +3255,10 @@ function analyzeInModi() { } } } +/** + * 此部分功能不再使用 + * @param map + */ let IGNORED_FOREIGN_KEY_MAP = {}; let IGNORED_RELATION_PATH_MAP = {}; let DEDUCED_RELATION_MAP = {}; @@ -3263,16 +3267,28 @@ let CREATE_FREE_ENTITIES = []; let UPDATE_FREE_ENTITIES = []; let FIXED_DESTINATION_PATH_MAP = {}; let FIXED_FOR_ALL_DESTINATION_PATH_ENTITIES = []; +/** + * 此函数不再使用 + * @param map + */ function registerIgnoredForeignKeyMap(map) { IGNORED_FOREIGN_KEY_MAP = map; } exports.registerIgnoredForeignKeyMap = registerIgnoredForeignKeyMap; +/** + * 此函数不再使用 + * @param map + */ function registerFreeEntities(selectFreeEntities = [], createFreeEntities = [], updateFreeEntities = []) { SELECT_FREE_ENTITIES = selectFreeEntities; CREATE_FREE_ENTITIES = createFreeEntities; UPDATE_FREE_ENTITIES = updateFreeEntities; } exports.registerFreeEntities = registerFreeEntities; +/** + * 此函数不再使用 + * @param map + */ function registerIgnoredRelationPathMap(map) { for (const k in map) { IGNORED_RELATION_PATH_MAP[(0, string_1.firstLetterUpperCase)(k)] = map[k]; @@ -3297,6 +3313,10 @@ function registerFixedDestinationPathMap(map) { } } exports.registerFixedDestinationPathMap = registerFixedDestinationPathMap; +/** + * 此函数不再使用 + * @param map + */ function registerDeducedRelationMap(map) { for (const k in map) { const entity = (0, string_1.firstLetterUpperCase)(k); @@ -3314,6 +3334,7 @@ function registerDeducedRelationMap(map) { exports.registerDeducedRelationMap = registerDeducedRelationMap; /** * 输出所有和User相关的对象的后继 + * 此函数不再使用 */ function outputRelation(outputDir, printer) { const ExcludedEntities = ['Oper', 'User', 'OperEntity', 'Modi', 'ModiEntity', 'UserRelation', 'Relation', 'RelationAuth', 'ActionAuth']; @@ -3464,6 +3485,47 @@ function outputRelation(outputDir, printer) { const filename = path_1.default.join(outputDir, 'Relation.ts'); (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); } +/** + * 输出oak-app-domain中的Relation.ts文件 + * 不再输出actionAuthGraph和relationAuthGraph两个复杂的对象 + * @param outputDir + * @param printer + */ +function outputRelation2(outputDir, printer) { + const entityRelations = []; + for (const entity in Schema) { + const { hasRelationDef } = Schema[entity]; + if (hasRelationDef) { + const { type } = hasRelationDef; + if (ts.isUnionTypeNode(type)) { + const { types } = type; + const relations = types.map(ele => { + (0, assert_1.default)(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal)); + return ele.literal.text; + }); + entityRelations.push([(0, string_1.firstLetterLowerCase)(entity), relations]); + } + else { + (0, assert_1.default)(ts.isLiteralTypeNode(type)); + (0, assert_1.default)(ts.isStringLiteral(type.literal)); + const relations = [type.literal.text]; + entityRelations.push([(0, string_1.firstLetterLowerCase)(entity), relations]); + } + } + } + const stmts = [ + factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("EntityDict"))])), factory.createStringLiteral("./EntityDict"), undefined), + factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, factory.createIdentifier("CreateOperationData"), factory.createIdentifier("Relation"))])), factory.createStringLiteral("./Relation/Schema"), undefined), + factory.createVariableStatement([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier("relations"), undefined, factory.createArrayTypeNode(factory.createTypeReferenceNode(factory.createIdentifier("Relation"), undefined)), factory.createArrayLiteralExpression((0, lodash_1.flatten)(entityRelations.map(([entity, relations]) => relations.map((relation) => factory.createObjectLiteralExpression([ + factory.createPropertyAssignment(factory.createIdentifier("id"), factory.createStringLiteral((0, uuid_1.formUuid)(entity, relation))), + factory.createPropertyAssignment(factory.createIdentifier("entity"), factory.createStringLiteral(entity)), + factory.createPropertyAssignment(factory.createIdentifier("name"), factory.createStringLiteral(relation)) + ], true)))), true))], ts.NodeFlags.Const)) + ]; + const result = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(stmts), ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, /*setParentNodes*/ false, ts.ScriptKind.TS)); + const filename = path_1.default.join(outputDir, 'Relation.ts'); + (0, fs_1.writeFileSync)(filename, result, { flag: 'w' }); +} function analyzeEntities(inputDir, relativePath) { const files = (0, fs_1.readdirSync)(inputDir); const fullFilenames = files.map(ele => { @@ -3492,7 +3554,7 @@ function buildSchema(outputDir) { outputAction(outputDir, printer); outputEntityDict(outputDir, printer); outputStorage(outputDir, printer); - outputRelation(outputDir, printer); + outputRelation2(outputDir, printer); outputIndexTs(outputDir); if (!process.env.COMPLING_AS_LIB) { outputPackageJson(outputDir); diff --git a/lib/index.d.ts b/lib/index.d.ts index 28fceb2..7da3ab3 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -1,4 +1,4 @@ -export { storageSchema, selectFreeEntities } from './base-app-domain'; +export { storageSchema } from './base-app-domain'; export type { EntityDict as BaseEntityDict } from './base-app-domain'; export * from './store/AsyncRowStore'; export * from './store/SyncRowStore'; diff --git a/lib/index.js b/lib/index.js index e42df61..a925976 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,10 +1,9 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.expandUuidTo36Bytes = exports.shrinkUuidTo32Bytes = exports.generateNewId = exports.generateNewIdAsync = exports.compareVersion = exports.checkAttributesScope = exports.checkAttributesNotNull = exports.composeUrl = exports.assert = exports.SimpleConnector = exports.selectFreeEntities = exports.storageSchema = void 0; +exports.expandUuidTo36Bytes = exports.shrinkUuidTo32Bytes = exports.generateNewId = exports.generateNewIdAsync = exports.compareVersion = exports.checkAttributesScope = exports.checkAttributesNotNull = exports.composeUrl = exports.assert = exports.SimpleConnector = exports.storageSchema = void 0; const tslib_1 = require("tslib"); var base_app_domain_1 = require("./base-app-domain"); Object.defineProperty(exports, "storageSchema", { enumerable: true, get: function () { return base_app_domain_1.storageSchema; } }); -Object.defineProperty(exports, "selectFreeEntities", { enumerable: true, get: function () { return base_app_domain_1.selectFreeEntities; } }); tslib_1.__exportStar(require("./store/AsyncRowStore"), exports); tslib_1.__exportStar(require("./store/SyncRowStore"), exports); tslib_1.__exportStar(require("./store/CascadeStore"), exports); diff --git a/lib/store/RelationAuth.d.ts b/lib/store/RelationAuth.d.ts index ecd7385..c95b928 100644 --- a/lib/store/RelationAuth.d.ts +++ b/lib/store/RelationAuth.d.ts @@ -1,27 +1,18 @@ import { EntityDict } from "../base-app-domain"; import { StorageSchema } from "../types"; -import { AuthCascadePath, EntityDict as BaseEntityDict, AuthDeduceRelationMap } from "../types/Entity"; +import { EntityDict as BaseEntityDict, AuthDeduceRelationMap } from "../types/Entity"; import { AsyncContext } from "./AsyncRowStore"; import { SyncContext } from "./SyncRowStore"; export declare class RelationAuth { - private actionCascadePathGraph; - private relationCascadePathGraph; private authDeduceRelationMap; private schema; static SPECIAL_ENTITIES: string[]; private selectFreeEntities; - private createFreeEntities; - private updateFreeEntities; - constructor(schema: StorageSchema, actionCascadePathGraph: AuthCascadePath[], relationCascadePathGraph: AuthCascadePath[], authDeduceRelationMap: AuthDeduceRelationMap, selectFreeEntities?: (keyof ED)[], createFreeEntities?: (keyof ED)[], updateFreeEntities?: (keyof ED)[]); + private updateFreeDict; + constructor(schema: StorageSchema, authDeduceRelationMap: AuthDeduceRelationMap, selectFreeEntities?: (keyof ED)[], updateFreeDict?: { + [A in keyof ED]?: string[]; + }); checkRelationSync>(entity: T, operation: Omit, context: Cxt): void; - /** - * 查询当前用户在对应entity上可以操作的relationIds - * @param entity - * @param entityId - * @param context - * @returns - */ - private getGrantedRelationIds; checkRelationAsync>(entity: T, operation: Omit, context: Cxt): Promise; private checkUserRelation; private checkOperateSpecialEntities2; diff --git a/lib/store/RelationAuth.js b/lib/store/RelationAuth.js index 484255c..63b2137 100644 --- a/lib/store/RelationAuth.js +++ b/lib/store/RelationAuth.js @@ -11,21 +11,15 @@ const action_1 = require("../actions/action"); const lodash_1 = require("../utils/lodash"); const env_1 = require("../compiler/env"); class RelationAuth { - actionCascadePathGraph; - relationCascadePathGraph; authDeduceRelationMap; schema; static SPECIAL_ENTITIES = env_1.SYSTEM_RESERVE_ENTITIES; selectFreeEntities; - createFreeEntities; - updateFreeEntities; - constructor(schema, actionCascadePathGraph, relationCascadePathGraph, authDeduceRelationMap, selectFreeEntities, createFreeEntities, updateFreeEntities) { - this.actionCascadePathGraph = actionCascadePathGraph; - this.relationCascadePathGraph = relationCascadePathGraph; + updateFreeDict; + constructor(schema, authDeduceRelationMap, selectFreeEntities, updateFreeDict) { this.schema = schema; this.selectFreeEntities = selectFreeEntities || []; - this.createFreeEntities = createFreeEntities || []; - this.updateFreeEntities = updateFreeEntities || []; + this.updateFreeDict = updateFreeDict || {}; this.authDeduceRelationMap = Object.assign({}, authDeduceRelationMap, { modi: 'entity', }); @@ -37,52 +31,6 @@ class RelationAuth { } this.checkActions2(entity, operation, context); } - /** - * 查询当前用户在对应entity上可以操作的relationIds - * @param entity - * @param entityId - * @param context - * @returns - */ - getGrantedRelationIds(entity, entityId, context) { - const result = context.select('relationAuth', { - data: { - id: 1, - destRelationId: 1, - destRelation: { - id: 1, - name: 1, - entity: 1, - entityId: 1, - display: 1, - }, - }, - filter: { - sourceRelation: { - userRelation$relation: { - userId: context.getCurrentUserId(), - } - }, - destRelation: { - entity: entity, - $or: [ - { - entityId, - }, - { - entityId: { - $exists: false, - }, - } - ], - }, - }, - }, {}); - if (result instanceof Promise) { - return result.then((r2) => r2.map(ele => ele.destRelation)); - } - return result.map(ele => ele.destRelation); - } // 后台检查filter是否满足relation约束 async checkRelationAsync(entity, operation, context) { if (context.isRoot()) { @@ -141,10 +89,6 @@ class RelationAuth { }, { dontCollect: true }); const checkRelationAuth = (relationAuth) => { const { destRelation, sourceRelationId, path } = relationAuth; - if (action === 'create' && path === '') { - // 自己建立自己,一定可以通过 - return 1; - } const destEntityFilter = {}; if (path) { (0, lodash_1.set)(destEntityFilter, path, { @@ -962,204 +906,9 @@ class RelationAuth { }; return checkNode(tree); } - /* private checkOperationTree | SyncContext>(tree: OperationTree, context: Cxt) { - const actionAuths2 = this.findActionAuthsOnNode(tree, context); - - const checkChildNode = (actionAuths: ED['actionAuth']['Schema'][] | Promise, node: OperationTree): boolean | Promise => { - const checkChildNodeInner = (legalAuths: ED['actionAuth']['Schema'][]) => { - // 因为如果children是数组的话,会把数组中所有的action并起来查询,所以在这里还要再确认一次 - - const realLegalPaths = legalAuths.filter( - (ele) => { - if (ele.destEntity === node.entity && ele.deActions.includes(node.action)) { - return true; - } - // 有一种例外情况,是在tree的根结点findActionAuthsOnNode时,deduce出了另外一个对象的权限,此时肯定可以通过,但不能再使用这条路径对children进行进一步判断了 - if (node === tree) { - return true; - } - return false; - } - ); - const checkChildren = () => { - const { children } = node; - const childPath = Object.keys(children); - if (childPath.length === 0) { - return true; - } - - const selfLegalPaths = realLegalPaths.filter( - (ele) => { - if (ele.destEntity === node.entity && ele.deActions.includes(node.action)) { - return true; - } - return false; - } - ); - // assert(selfLegalPaths.length > 0, `对象${node.entity as string}的权限检查是用deduce的对象通过的,无法再进一步对子对象加以判断`); - const childResult = childPath.map( - (childPath) => { - const child = children[childPath]; - const childEntity = child instanceof Array ? child[0].entity : child.entity; - // 这里如果该子结点能deduce到父,则直接通过 - if (this.authDeduceRelationMap[childEntity]) { - assert(this.authDeduceRelationMap[childEntity] === 'entity'); - const rel = judgeRelation(this.schema, childEntity, childPath); - if (rel === 2) { - return true; - } - } - - const pathToParent = childPath.endsWith('$entity') ? node.entity as string : childPath.split('$')[1]; - if (child instanceof Array) { - const childActions = child.map(ele => ele.action); - const childLegalAuths = selfLegalPaths.map( - (ele) => { - const { paths, relationId } = ele; - const paths2 = paths.map( - (path) => path ? `${pathToParent}.${path}` : pathToParent - ); - return context.select('actionAuth', { - data: { - id: 1, - }, - filter: { - paths: { - $overlaps: paths2, - }, - destEntity: childEntity as string, - deActions: { - $overlaps: childActions, - }, - relationId: relationId || { - $exists: false, - }, - } - }, { dontCollect: true }) - } - ).flat() as ED['actionAuth']['Schema'][] | Promise[]; - if (childLegalAuths[0] instanceof Promise) { - return Promise.all(childLegalAuths).then( - (clas) => child.map( - (c) => checkChildNode(clas, c) - ) - ) - } - return child.map( - (c) => checkChildNode(childLegalAuths as ED['actionAuth']['Schema'][], c) - ); - } - - const childLegalAuths = realLegalPaths.map( - (ele) => { - const { paths, relationId } = ele; - const paths2 = paths.map( - (path) => path ? `${pathToParent}.${path}` : pathToParent - ); - return context.select('actionAuth', { - data: { - id: 1, - }, - filter: { - paths: { - $overlaps: paths2, - }, - destEntity: childEntity as string, - deActions: { - $overlaps: child.action, - }, - relationId: relationId || { - $exists: false, - }, - } - }, { dontCollect: true }) - } - ).flat() as ED['actionAuth']['Schema'][] | Promise[]; - - if (childLegalAuths[0] instanceof Promise) { - return Promise.all(childLegalAuths).then( - (clas) => checkChildNode(clas.flat(), child) - ); - } - return checkChildNode(childLegalAuths as ED['actionAuth']['Schema'][], child); - } - ).flat(); - - if (childResult[0] instanceof Promise) { - return Promise.all(childResult).then( - (r) => !r.includes(false) - ); - } - return !childResult.includes(false); - }; - - if (RelationAuth.SPECIAL_ENTITIES.includes(node.entity as string)) { - // 特殊entity走特别的路径判断 - const result = this.checkOperateSpecialEntities2(node.entity, node.action, node.filter, context); - - if (result instanceof Promise) { - return result.then( - (r) => { - if (r) { - return checkChildren(); - } - return false; - } - ); - } - if (result) { - if (node.entity === 'user') { - // 如果当前是对user对象操作,需要加上一个指向它自身的actionAuth,否则剩下的子对象会判定不过 - // user的操作权限由应用自己决定,如果user的操作最终过不去,这里放过也没关系 - assert(node === tree && realLegalPaths.length === 0); // user不可能是非根结点 - realLegalPaths.push({ - id: 'temp', - paths: [''], - $$createAt$$: 1, - $$updateAt$$: 1, - $$seq$$: 'temp', - destEntity: 'user', - deActions: [node.action], - }); - } - return checkChildren(); - } - if (process.env.NODE_ENV === 'development') { - console.warn('对象operate权限检查不通过', node); - } - return false; - } - - if (realLegalPaths.length === 0) { - if (node === tree) { - if (process.env.NODE_ENV === 'development') { - console.warn('对象operate权限检查不通过', node); - } - return false; - } - // 如果不是tree的根结点,相对路径上的actionAuth找不到,还可以尝试从自身的filter去重试其它路径 - return this.checkOperationTree(node, context); - } - - return checkChildren(); - }; - - if (actionAuths instanceof Promise) { - return actionAuths.then( - (aars) => checkChildNodeInner(aars) - ); - } - return checkChildNodeInner(actionAuths); - }; - - return checkChildNode(actionAuths2, tree); - } */ checkOperation(entity, operation, context) { const { action, filter, data } = operation; - if (action === 'create' && this.createFreeEntities.includes(entity)) { - return true; - } - else if (action === 'update' && this.updateFreeEntities.includes(entity)) { + if (this.updateFreeDict[entity] && this.updateFreeDict[entity].includes(action)) { return true; } const userId = context.getCurrentUserId(); diff --git a/lib/types/Entity.d.ts b/lib/types/Entity.d.ts index 1da3e19..068c10f 100644 --- a/lib/types/Entity.d.ts +++ b/lib/types/Entity.d.ts @@ -185,11 +185,13 @@ export type Configuration = { actionType?: ActionType; static?: boolean; }; -export type AuthCascadePath = [keyof ED, string, keyof ED, boolean]; export type AuthDeduceRelationMap = { [T in keyof ED]?: keyof ED[T]['OpSchema']; }; export type SelectFreeEntities = (keyof ED)[]; +export type UpdateFreeDict = { + [A in keyof ED]?: string[]; +}; export type OtmKey = K | `${K}$${number}`; export interface SubDataDef { id: string; diff --git a/src/checkers/index.ts b/src/checkers/index.ts index 38099e8..657b8fa 100644 --- a/src/checkers/index.ts +++ b/src/checkers/index.ts @@ -3,7 +3,7 @@ import { AsyncContext } from '../store/AsyncRowStore'; import { createRemoveCheckers, createCreateCheckers } from '../store/checker'; import { createModiRelatedCheckers } from '../store/modi'; import { SyncContext } from '../store/SyncRowStore'; -import { StorageSchema, EntityDict as BaseEntityDict, Checker, AuthCascadePath, CascadeRemoveDefDict } from '../types'; +import { StorageSchema, EntityDict as BaseEntityDict, Checker } from '../types'; export function createDynamicCheckers | SyncContext>(schema: StorageSchema) { const checkers: Checker[] = []; diff --git a/src/compiler/schemalBuilder.ts b/src/compiler/schemalBuilder.ts index 185a70d..baa6191 100644 --- a/src/compiler/schemalBuilder.ts +++ b/src/compiler/schemalBuilder.ts @@ -6236,6 +6236,10 @@ function analyzeInModi() { } +/** + * 此部分功能不再使用 + * @param map + */ let IGNORED_FOREIGN_KEY_MAP: Record = {}; let IGNORED_RELATION_PATH_MAP: Record = {}; let DEDUCED_RELATION_MAP: Record = {}; @@ -6245,10 +6249,18 @@ let UPDATE_FREE_ENTITIES: string[] = []; let FIXED_DESTINATION_PATH_MAP: Record = {}; let FIXED_FOR_ALL_DESTINATION_PATH_ENTITIES: string[] = []; +/** + * 此函数不再使用 + * @param map + */ export function registerIgnoredForeignKeyMap(map: Record) { IGNORED_FOREIGN_KEY_MAP = map; } +/** + * 此函数不再使用 + * @param map + */ export function registerFreeEntities( selectFreeEntities: string[] = [], createFreeEntities: string[] = [], @@ -6258,6 +6270,10 @@ export function registerFreeEntities( UPDATE_FREE_ENTITIES = updateFreeEntities; } +/** + * 此函数不再使用 + * @param map + */ export function registerIgnoredRelationPathMap(map: Record) { for (const k in map) { IGNORED_RELATION_PATH_MAP[firstLetterUpperCase(k)] = map[k]; @@ -6282,6 +6298,10 @@ export function registerFixedDestinationPathMap(map: Record) { } } +/** + * 此函数不再使用 + * @param map + */ export function registerDeducedRelationMap(map: Record) { for (const k in map) { const entity = firstLetterUpperCase(k); @@ -6303,6 +6323,7 @@ export function registerDeducedRelationMap(map: Record) { /** * 输出所有和User相关的对象的后继 + * 此函数不再使用 */ function outputRelation(outputDir: string, printer: ts.Printer) { const ExcludedEntities = ['Oper', 'User', 'OperEntity', 'Modi', 'ModiEntity', 'UserRelation', 'Relation', 'RelationAuth', 'ActionAuth']; @@ -6719,6 +6740,114 @@ function outputRelation(outputDir: string, printer: ts.Printer) { writeFileSync(filename, result, { flag: 'w' }); } +/** + * 输出oak-app-domain中的Relation.ts文件 + * 不再输出actionAuthGraph和relationAuthGraph两个复杂的对象 + * @param outputDir + * @param printer + */ +function outputRelation2(outputDir: string, printer: ts.Printer) { + const entityRelations: [string, string[]][] = []; + for (const entity in Schema) { + const { hasRelationDef } = Schema[entity]; + if (hasRelationDef) { + const { type } = hasRelationDef; + if (ts.isUnionTypeNode(type)) { + const { types } = type; + const relations = types.map( + ele => { + assert(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal)); + return ele.literal.text; + } + ); + entityRelations.push([firstLetterLowerCase(entity), relations]); + } + else { + assert(ts.isLiteralTypeNode(type)); + assert(ts.isStringLiteral(type.literal)); + const relations = [type.literal.text]; + entityRelations.push([firstLetterLowerCase(entity), relations]); + } + } + } + + const stmts: ts.Statement[] = [ + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("EntityDict") + )]) + ), + factory.createStringLiteral("./EntityDict"), + undefined + ), + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([factory.createImportSpecifier( + false, + factory.createIdentifier("CreateOperationData"), + factory.createIdentifier("Relation") + )]) + ), + factory.createStringLiteral("./Relation/Schema"), + undefined + ), + factory.createVariableStatement( + [factory.createToken(ts.SyntaxKind.ExportKeyword)], + factory.createVariableDeclarationList( + [factory.createVariableDeclaration( + factory.createIdentifier("relations"), + undefined, + factory.createArrayTypeNode(factory.createTypeReferenceNode( + factory.createIdentifier("Relation"), + undefined + )), + factory.createArrayLiteralExpression( + flatten(entityRelations.map( + ([entity, relations]) => relations.map( + (relation) => factory.createObjectLiteralExpression( + [ + factory.createPropertyAssignment( + factory.createIdentifier("id"), + factory.createStringLiteral(formUuid(entity, relation)) + ), + factory.createPropertyAssignment( + factory.createIdentifier("entity"), + factory.createStringLiteral(entity) + ), + factory.createPropertyAssignment( + factory.createIdentifier("name"), + factory.createStringLiteral(relation) + ) + ], + true + ) + ) + )), + true + ) + )], + ts.NodeFlags.Const + ) + ) + ]; + + const result = printer.printList( + ts.ListFormat.SourceFileStatements, + factory.createNodeArray(stmts), + ts.createSourceFile("someFileName.ts", "", ts.ScriptTarget.Latest, /*setParentNodes*/ false, ts.ScriptKind.TS)); + const filename = PathLib.join(outputDir, 'Relation.ts'); + writeFileSync(filename, result, { flag: 'w' }); +} + export function analyzeEntities(inputDir: string, relativePath?: string) { const files = readdirSync(inputDir); const fullFilenames = files.map( @@ -6755,7 +6884,7 @@ export function buildSchema(outputDir: string): void { outputAction(outputDir, printer); outputEntityDict(outputDir, printer); outputStorage(outputDir, printer); - outputRelation(outputDir, printer); + outputRelation2(outputDir, printer); outputIndexTs(outputDir); if (!process.env.COMPLING_AS_LIB) { diff --git a/src/index.ts b/src/index.ts index 5792ec3..52466c1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ -export { storageSchema, selectFreeEntities } from './base-app-domain'; +export { storageSchema } from './base-app-domain'; export type { EntityDict as BaseEntityDict } from './base-app-domain'; diff --git a/src/store/RelationAuth.ts b/src/store/RelationAuth.ts index 9ca762e..8f0f2b1 100644 --- a/src/store/RelationAuth.ts +++ b/src/store/RelationAuth.ts @@ -1,7 +1,7 @@ import assert from "assert"; import { EntityDict } from "../base-app-domain"; -import { OakException, OakUniqueViolationException, OakUnloggedInException, OakUserInvisibleException, OakUserUnpermittedException, StorageSchema } from "../types"; -import { AuthCascadePath, EntityDict as BaseEntityDict, AuthDeduceRelationMap } from "../types/Entity"; +import { OakUnloggedInException, OakUserInvisibleException, OakUserUnpermittedException, StorageSchema } from "../types"; +import { EntityDict as BaseEntityDict, AuthDeduceRelationMap } from "../types/Entity"; import { AsyncContext } from "./AsyncRowStore"; import { checkFilterContains, combineFilters } from "./filter"; import { judgeRelation } from "./relation"; @@ -34,30 +34,25 @@ type CheckRelationResult = { }; export class RelationAuth{ - private actionCascadePathGraph: AuthCascadePath[]; - private relationCascadePathGraph: AuthCascadePath[]; private authDeduceRelationMap: AuthDeduceRelationMap; private schema: StorageSchema; static SPECIAL_ENTITIES = SYSTEM_RESERVE_ENTITIES; private selectFreeEntities: (keyof ED)[]; - private createFreeEntities: (keyof ED)[]; - private updateFreeEntities: (keyof ED)[]; + private updateFreeDict: { + [A in keyof ED]?: string[]; + }; constructor(schema: StorageSchema, - actionCascadePathGraph: AuthCascadePath[], - relationCascadePathGraph: AuthCascadePath[], authDeduceRelationMap: AuthDeduceRelationMap, selectFreeEntities?: (keyof ED)[], - createFreeEntities?: (keyof ED)[], - updateFreeEntities?: (keyof ED)[]) { - this.actionCascadePathGraph = actionCascadePathGraph; - this.relationCascadePathGraph = relationCascadePathGraph; + updateFreeDict?: { + [A in keyof ED]?: string[]; + }) { this.schema = schema; this.selectFreeEntities = selectFreeEntities || []; - this.createFreeEntities = createFreeEntities || []; - this.updateFreeEntities = updateFreeEntities || []; + this.updateFreeDict = updateFreeDict || {}; this.authDeduceRelationMap = Object.assign({}, authDeduceRelationMap, { modi: 'entity', }); @@ -77,55 +72,6 @@ export class RelationAuth{ this.checkActions2(entity, operation, context); } - /** - * 查询当前用户在对应entity上可以操作的relationIds - * @param entity - * @param entityId - * @param context - * @returns - */ - private getGrantedRelationIds | SyncContext>(entity: keyof ED, entityId: string, context: Cxt) { - const result = context.select('relationAuth', { - data: { - id: 1, - destRelationId: 1, - destRelation: { - id: 1, - name: 1, - entity: 1, - entityId: 1, - display: 1, - }, - }, - filter: { - sourceRelation: { - userRelation$relation: { - userId: context.getCurrentUserId(), - } - }, - destRelation: { - entity: entity as string, - $or: [ - { - entityId, - }, - { - entityId: { - $exists: false, - }, - } - ], - }, - }, - }, {}); - if (result instanceof Promise) { - return result.then( - (r2) => r2.map(ele => ele.destRelation!) - ); - } - return result.map(ele => ele.destRelation!); - } - // 后台检查filter是否满足relation约束 async checkRelationAsync>(entity: T, operation: Omit, context: Cxt) { @@ -190,11 +136,6 @@ export class RelationAuth{ const checkRelationAuth = (relationAuth: ED['relationAuth']['Schema']) => { const { destRelation, sourceRelationId, path } = relationAuth; - if (action === 'create' && path === '') { - // 自己建立自己,一定可以通过 - return 1; - } - const destEntityFilter: ED[keyof ED]['Selection']['filter'] = {}; if (path) { @@ -1266,198 +1207,6 @@ export class RelationAuth{ return checkNode(tree); } - /* private checkOperationTree | SyncContext>(tree: OperationTree, context: Cxt) { - const actionAuths2 = this.findActionAuthsOnNode(tree, context); - - const checkChildNode = (actionAuths: ED['actionAuth']['Schema'][] | Promise, node: OperationTree): boolean | Promise => { - const checkChildNodeInner = (legalAuths: ED['actionAuth']['Schema'][]) => { - // 因为如果children是数组的话,会把数组中所有的action并起来查询,所以在这里还要再确认一次 - - const realLegalPaths = legalAuths.filter( - (ele) => { - if (ele.destEntity === node.entity && ele.deActions.includes(node.action)) { - return true; - } - // 有一种例外情况,是在tree的根结点findActionAuthsOnNode时,deduce出了另外一个对象的权限,此时肯定可以通过,但不能再使用这条路径对children进行进一步判断了 - if (node === tree) { - return true; - } - return false; - } - ); - const checkChildren = () => { - const { children } = node; - const childPath = Object.keys(children); - if (childPath.length === 0) { - return true; - } - - const selfLegalPaths = realLegalPaths.filter( - (ele) => { - if (ele.destEntity === node.entity && ele.deActions.includes(node.action)) { - return true; - } - return false; - } - ); - // assert(selfLegalPaths.length > 0, `对象${node.entity as string}的权限检查是用deduce的对象通过的,无法再进一步对子对象加以判断`); - const childResult = childPath.map( - (childPath) => { - const child = children[childPath]; - const childEntity = child instanceof Array ? child[0].entity : child.entity; - // 这里如果该子结点能deduce到父,则直接通过 - if (this.authDeduceRelationMap[childEntity]) { - assert(this.authDeduceRelationMap[childEntity] === 'entity'); - const rel = judgeRelation(this.schema, childEntity, childPath); - if (rel === 2) { - return true; - } - } - - const pathToParent = childPath.endsWith('$entity') ? node.entity as string : childPath.split('$')[1]; - if (child instanceof Array) { - const childActions = child.map(ele => ele.action); - const childLegalAuths = selfLegalPaths.map( - (ele) => { - const { paths, relationId } = ele; - const paths2 = paths.map( - (path) => path ? `${pathToParent}.${path}` : pathToParent - ); - return context.select('actionAuth', { - data: { - id: 1, - }, - filter: { - paths: { - $overlaps: paths2, - }, - destEntity: childEntity as string, - deActions: { - $overlaps: childActions, - }, - relationId: relationId || { - $exists: false, - }, - } - }, { dontCollect: true }) - } - ).flat() as ED['actionAuth']['Schema'][] | Promise[]; - if (childLegalAuths[0] instanceof Promise) { - return Promise.all(childLegalAuths).then( - (clas) => child.map( - (c) => checkChildNode(clas, c) - ) - ) - } - return child.map( - (c) => checkChildNode(childLegalAuths as ED['actionAuth']['Schema'][], c) - ); - } - - const childLegalAuths = realLegalPaths.map( - (ele) => { - const { paths, relationId } = ele; - const paths2 = paths.map( - (path) => path ? `${pathToParent}.${path}` : pathToParent - ); - return context.select('actionAuth', { - data: { - id: 1, - }, - filter: { - paths: { - $overlaps: paths2, - }, - destEntity: childEntity as string, - deActions: { - $overlaps: child.action, - }, - relationId: relationId || { - $exists: false, - }, - } - }, { dontCollect: true }) - } - ).flat() as ED['actionAuth']['Schema'][] | Promise[]; - - if (childLegalAuths[0] instanceof Promise) { - return Promise.all(childLegalAuths).then( - (clas) => checkChildNode(clas.flat(), child) - ); - } - return checkChildNode(childLegalAuths as ED['actionAuth']['Schema'][], child); - } - ).flat(); - - if (childResult[0] instanceof Promise) { - return Promise.all(childResult).then( - (r) => !r.includes(false) - ); - } - return !childResult.includes(false); - }; - - if (RelationAuth.SPECIAL_ENTITIES.includes(node.entity as string)) { - // 特殊entity走特别的路径判断 - const result = this.checkOperateSpecialEntities2(node.entity, node.action, node.filter, context); - - if (result instanceof Promise) { - return result.then( - (r) => { - if (r) { - return checkChildren(); - } - return false; - } - ); - } - if (result) { - if (node.entity === 'user') { - // 如果当前是对user对象操作,需要加上一个指向它自身的actionAuth,否则剩下的子对象会判定不过 - // user的操作权限由应用自己决定,如果user的操作最终过不去,这里放过也没关系 - assert(node === tree && realLegalPaths.length === 0); // user不可能是非根结点 - realLegalPaths.push({ - id: 'temp', - paths: [''], - $$createAt$$: 1, - $$updateAt$$: 1, - $$seq$$: 'temp', - destEntity: 'user', - deActions: [node.action], - }); - } - return checkChildren(); - } - if (process.env.NODE_ENV === 'development') { - console.warn('对象operate权限检查不通过', node); - } - return false; - } - - if (realLegalPaths.length === 0) { - if (node === tree) { - if (process.env.NODE_ENV === 'development') { - console.warn('对象operate权限检查不通过', node); - } - return false; - } - // 如果不是tree的根结点,相对路径上的actionAuth找不到,还可以尝试从自身的filter去重试其它路径 - return this.checkOperationTree(node, context); - } - - return checkChildren(); - }; - - if (actionAuths instanceof Promise) { - return actionAuths.then( - (aars) => checkChildNodeInner(aars) - ); - } - return checkChildNodeInner(actionAuths); - }; - - return checkChildNode(actionAuths2, tree); - } */ private checkOperation | SyncContext>( entity: T, @@ -1465,11 +1214,8 @@ export class RelationAuth{ context: Cxt, ) { const { action, filter, data } = operation; - if (action === 'create' && this.createFreeEntities.includes(entity)) { - return true; - } - else if (action === 'update' && this.updateFreeEntities.includes(entity)) { - return true; + if (this.updateFreeDict[entity] && this.updateFreeDict[entity]!.includes(action)) { + return true; } const userId = context.getCurrentUserId(); if (!userId) { diff --git a/src/types/Entity.ts b/src/types/Entity.ts index b1e3cec..9b3d413 100644 --- a/src/types/Entity.ts +++ b/src/types/Entity.ts @@ -262,11 +262,13 @@ export type Configuration = { static?: boolean; // 标识是维表(变动较小,相对独立) }; -export type AuthCascadePath = [keyof ED, string, keyof ED, boolean]; export type AuthDeduceRelationMap = { [T in keyof ED]?: keyof ED[T]['OpSchema']; }; export type SelectFreeEntities = (keyof ED)[]; +export type UpdateFreeDict = { + [A in keyof ED]?: string[]; +}; // 一对多的键值的扩展 export type OtmKey = K | `${K}$${number}`;