checker中对actionAuth的处理,未完成

This commit is contained in:
Xu Chang 2023-01-17 19:09:57 +08:00
parent aa889f1abc
commit bd8fd47df5
17 changed files with 597 additions and 621 deletions

View File

@ -1,5 +1,5 @@
import { EntityDict } from '../base-app-domain'; import { EntityDict } from '../base-app-domain';
import { AsyncContext } from '../store/AsyncRowStore'; import { AsyncContext } from '../store/AsyncRowStore';
import { SyncContext } from '../store/SyncRowStore'; import { SyncContext } from '../store/SyncRowStore';
import { StorageSchema, EntityDict as BaseEntityDict, Checker } from '../types'; import { StorageSchema, EntityDict as BaseEntityDict, Checker, AuthDefDict } from '../types';
export declare function createDynamicCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>): Checker<ED, keyof ED, Cxt>[]; export declare function createDynamicCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict?: AuthDefDict<ED>): Checker<ED, keyof ED, Cxt>[];

View File

@ -4,10 +4,12 @@ exports.createDynamicCheckers = void 0;
var tslib_1 = require("tslib"); var tslib_1 = require("tslib");
var checker_1 = require("../store/checker"); var checker_1 = require("../store/checker");
var modi_1 = require("../store/modi"); var modi_1 = require("../store/modi");
function createDynamicCheckers(schema) { function createDynamicCheckers(schema, authDict) {
var checkers = []; var checkers = [];
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, modi_1.createModiRelatedCheckers)(schema)), false)); checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, modi_1.createModiRelatedCheckers)(schema)), false));
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, checker_1.createRelationHierarchyCheckers)(schema)), false)); if (authDict) {
checkers.push.apply(checkers, tslib_1.__spreadArray([], tslib_1.__read((0, checker_1.createAuthCheckers)(schema, authDict)), false));
}
return checkers; return checkers;
} }
exports.createDynamicCheckers = createDynamicCheckers; exports.createDynamicCheckers = createDynamicCheckers;

View File

@ -337,8 +337,8 @@ function analyzeEntity(filename, path, program, relativePath) {
var localEnumStringTypes = []; var localEnumStringTypes = [];
var additionalImports = []; var additionalImports = [];
var localeDef = undefined; var localeDef = undefined;
var relationHierarchy = undefined; // let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
var reverseCascadeRelationHierarchy = undefined; // let reverseCascadeRelationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
ts.forEachChild(sourceFile, function (node) { ts.forEachChild(sourceFile, function (node) {
var _a, _b, _c, _d; var _a, _b, _c, _d;
if (ts.isImportDeclaration(node)) { if (ts.isImportDeclaration(node)) {
@ -796,22 +796,22 @@ function analyzeEntity(filename, path, program, relativePath) {
_static = true; // static如果有值只能为true _static = true; // static如果有值只能为true
} }
} }
else if (ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'RelationHierarchy') { /* else if (ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'RelationHierarchy') {
// RelationHierary // RelationHierary
(0, assert_1.default)(hasRelationDef, "".concat(moduleName, "\u4E2D\u7684Relation\u5B9A\u4E49\u5728RelationHierarchy\u4E4B\u540E")); assert(hasRelationDef, `${moduleName}中的Relation定义在RelationHierarchy之后`);
var initializer = declaration.initializer; const { initializer } = declaration;
(0, assert_1.default)(ts.isObjectLiteralExpression(initializer), "".concat(moduleName, "\u4E2D\u7684RelationHierarchy\u7684\u5B9A\u4E49\u5FC5\u987B\u662F\u521D\u59CB\u5316\u4E3AObjectLiteralExpress")); assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`);
relationHierarchy = initializer; relationHierarchy = initializer;
} }
else if (ts.isTypeReferenceNode(declaration.type) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'ReverseCascadeRelationHierarchy') { else if (ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'ReverseCascadeRelationHierarchy') {
// ReverseCascadeRelationHierarchy // ReverseCascadeRelationHierarchy
(0, assert_1.default)(hasRelationDef, "".concat(moduleName, "\u4E2D\u7684Relation\u5B9A\u4E49\u5728ReverseCascadeRelationHierarchy\u4E4B\u540E")); assert(hasRelationDef, `${moduleName}中的Relation定义在ReverseCascadeRelationHierarchy之后`);
var initializer = declaration.initializer; const { initializer } = declaration;
(0, assert_1.default)(ts.isObjectLiteralExpression(initializer), "".concat(moduleName, "\u4E2D\u7684RelationHierarchy\u7684\u5B9A\u4E49\u5FC5\u987B\u662F\u521D\u59CB\u5316\u4E3AObjectLiteralExpress")); assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`);
reverseCascadeRelationHierarchy = initializer; reverseCascadeRelationHierarchy = initializer;
} } */
else { else {
throw new Error("".concat(moduleName, "\uFF1A\u4E0D\u80FD\u7406\u89E3\u7684\u5B9A\u4E49\u5185\u5BB9").concat(declaration.name.getText())); throw new Error("".concat(moduleName, "\uFF1A\u4E0D\u80FD\u7406\u89E3\u7684\u5B9A\u4E49\u5185\u5BB9").concat(declaration.name.text));
} }
}); });
} }
@ -851,25 +851,25 @@ function analyzeEntity(filename, path, program, relativePath) {
locale: localeDef, locale: localeDef,
}); });
} }
if (hasRelationDef) { /* if (hasRelationDef) {
if (!relationHierarchy && !reverseCascadeRelationHierarchy) { if(!relationHierarchy && !reverseCascadeRelationHierarchy){
console.warn("".concat(filename, "\u4E2D\u5B9A\u4E49\u4E86Relation,\u4F46\u5E76\u6CA1\u6709relationHierarchy\u6216reverseCascadeRelationHierarchy\u7684\u5B9A\u4E49\uFF0C\u8BF7\u6CE8\u610F\u81EA\u4E3B\u7F16\u5199\u6743\u9650\u5206\u914D\u7684checker")); console.warn(`${filename}中定义了Relation,但并没有relationHierarchy或reverseCascadeRelationHierarchy的定义请注意自主编写权限分配的checker`);
} }
if (relationHierarchy) { if (relationHierarchy) {
(0, lodash_1.assign)(schema, { assign(schema, {
relationHierarchy: relationHierarchy, relationHierarchy,
}); });
} }
if (reverseCascadeRelationHierarchy) { if (reverseCascadeRelationHierarchy) {
(0, lodash_1.assign)(schema, { assign(schema, {
reverseCascadeRelationHierarchy: reverseCascadeRelationHierarchy, reverseCascadeRelationHierarchy,
}); });
} }
} }
else { else {
(0, assert_1.default)(!relationHierarchy, "".concat(filename, "\u4E2D\u5177\u6709relationHierarchy\u5B9A\u4E49\u4F46\u6CA1\u6709Relation\u5B9A\u4E49")); assert(!relationHierarchy, `${filename}中具有relationHierarchy定义但没有Relation定义`);
(0, assert_1.default)(!reverseCascadeRelationHierarchy, "".concat(filename, "\u4E2D\u5177\u6709reverseCascadeRelationHierarchy\u5B9A\u4E49\u4F46\u6CA1\u6709Relation\u5B9A\u4E49")); assert(!reverseCascadeRelationHierarchy, `${filename}中具有reverseCascadeRelationHierarchy定义但没有Relation定义`)
} } */
(0, lodash_1.assign)(Schema, (_a = {}, (0, lodash_1.assign)(Schema, (_a = {},
_a[moduleName] = schema, _a[moduleName] = schema,
_a)); _a));
@ -3066,13 +3066,19 @@ function outputStorage(outputDir, printer) {
var entityAssignments = []; var entityAssignments = [];
for (var entity in Schema) { for (var entity in Schema) {
var indexExpressions = []; var indexExpressions = [];
var _a = Schema[entity], sourceFile = _a.sourceFile, inModi = _a.inModi, indexes = _a.indexes, toModi = _a.toModi, actionType = _a.actionType, _static = _a.static, relationHierarchy = _a.relationHierarchy, reverseCascadeRelationHierarchy = _a.reverseCascadeRelationHierarchy; var _a = Schema[entity], sourceFile = _a.sourceFile, inModi = _a.inModi, indexes = _a.indexes, toModi = _a.toModi, actionType = _a.actionType, _static = _a.static, hasRelationDef = _a.hasRelationDef;
var fromSchemaSpecifiers = [ var fromSchemaSpecifiers = [
factory.createImportSpecifier(false, undefined, factory.createIdentifier("OpSchema")) factory.createImportSpecifier(false, undefined, factory.createIdentifier("OpSchema"))
]; ];
if (relationHierarchy || reverseCascadeRelationHierarchy) { /* if (relationHierarchy || reverseCascadeRelationHierarchy) {
fromSchemaSpecifiers.push(factory.createImportSpecifier(false, undefined, factory.createIdentifier("Relation"))); fromSchemaSpecifiers.push(
} factory.createImportSpecifier(
false,
undefined,
factory.createIdentifier("Relation")
)
);
} */
var statements = [ var statements = [
factory.createImportDeclaration(undefined, undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("StorageDesc"))])), factory.createStringLiteral("".concat((0, env_1.TYPE_PATH_IN_OAK_DOMAIN)(), "Storage")), undefined), factory.createImportDeclaration(undefined, undefined, factory.createImportClause(false, undefined, factory.createNamedImports([factory.createImportSpecifier(false, undefined, factory.createIdentifier("StorageDesc"))])), factory.createStringLiteral("".concat((0, env_1.TYPE_PATH_IN_OAK_DOMAIN)(), "Storage")), undefined),
factory.createImportDeclaration(undefined, undefined, factory.createImportClause(false, undefined, factory.createNamedImports(fromSchemaSpecifiers)), factory.createStringLiteral("./Schema"), undefined) factory.createImportDeclaration(undefined, undefined, factory.createImportClause(false, undefined, factory.createNamedImports(fromSchemaSpecifiers)), factory.createStringLiteral("./Schema"), undefined)
@ -3131,18 +3137,35 @@ function outputStorage(outputDir, printer) {
if (indexExpressions.length > 0) { if (indexExpressions.length > 0) {
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("indexes"), factory.createArrayLiteralExpression(indexExpressions, true))); propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("indexes"), factory.createArrayLiteralExpression(indexExpressions, true)));
} }
if (relationHierarchy) { /* if (relationHierarchy) {
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("relationHierarchy"), relationHierarchy)); propertyAssignments.push(
factory.createPropertyAssignment(
factory.createIdentifier("relationHierarchy"),
relationHierarchy,
)
);
} }
if (reverseCascadeRelationHierarchy) { if (reverseCascadeRelationHierarchy) {
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("reverseCascadeRelationHierarchy"), reverseCascadeRelationHierarchy)); propertyAssignments.push(
factory.createPropertyAssignment(
factory.createIdentifier("reverseCascadeRelationHierarchy"),
reverseCascadeRelationHierarchy,
)
);
} */
if (hasRelationDef) {
var type = hasRelationDef.type;
(0, assert_1.default)(ts.isUnionTypeNode(type));
var types = type.types;
var relationTexts = types.map(function (ele) {
(0, assert_1.default)(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal));
return ele.literal.text;
});
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("relation"), factory.createArrayLiteralExpression(relationTexts.map(function (ele) { return factory.createStringLiteral(ele); }))));
} }
var sdTypeArguments = [ var sdTypeArguments = [
factory.createTypeReferenceNode(factory.createIdentifier("OpSchema"), undefined) factory.createTypeReferenceNode(factory.createIdentifier("OpSchema"), undefined)
]; ];
if (relationHierarchy) {
sdTypeArguments.push(factory.createTypeReferenceNode(factory.createIdentifier("Relation"), undefined));
}
statements.push(factory.createVariableStatement([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier("desc"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("StorageDesc"), sdTypeArguments), factory.createObjectLiteralExpression(propertyAssignments, true))], ts.NodeFlags.Const))); statements.push(factory.createVariableStatement([factory.createModifier(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier("desc"), undefined, factory.createTypeReferenceNode(factory.createIdentifier("StorageDesc"), sdTypeArguments), factory.createObjectLiteralExpression(propertyAssignments, true))], ts.NodeFlags.Const)));
var result_2 = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), sourceFile); var result_2 = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), sourceFile);
var filename_1 = path_1.default.join(outputDir, entity, 'Storage.ts'); var filename_1 = path_1.default.join(outputDir, entity, 'Storage.ts');

View File

@ -1,7 +1,7 @@
import { Checker, EntityDict, OperateOption, SelectOption, StorageSchema, Trigger } from "../types"; import { AuthDefDict, Checker, EntityDict, OperateOption, SelectOption, StorageSchema, Trigger } from "../types";
import { EntityDict as BaseEntityDict } from '../base-app-domain'; import { EntityDict as BaseEntityDict } from '../base-app-domain';
import { AsyncContext } from "./AsyncRowStore"; import { AsyncContext } from "./AsyncRowStore";
import { SyncContext } from './SyncRowStore'; import { SyncContext } from './SyncRowStore';
export declare function translateCheckerInAsyncContext<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(checker: Checker<ED, keyof ED, Cxt>): Trigger<ED, keyof ED, Cxt>['fn']; export declare function translateCheckerInAsyncContext<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(checker: Checker<ED, keyof ED, Cxt>): Trigger<ED, keyof ED, Cxt>['fn'];
export declare function translateCheckerInSyncContext<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends SyncContext<ED>>(checker: Checker<ED, T, Cxt>): (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => void; export declare function translateCheckerInSyncContext<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends SyncContext<ED>>(checker: Checker<ED, T, Cxt>): (operation: ED[T]['Operation'], context: Cxt, option: OperateOption | SelectOption) => void;
export declare function createRelationHierarchyCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>): Checker<ED, keyof ED, Cxt>[]; export declare function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict: AuthDefDict<ED>): Checker<ED, keyof ED, Cxt>[];

View File

@ -1,6 +1,6 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.createRelationHierarchyCheckers = exports.translateCheckerInSyncContext = exports.translateCheckerInAsyncContext = void 0; exports.createAuthCheckers = exports.translateCheckerInSyncContext = exports.translateCheckerInAsyncContext = void 0;
var tslib_1 = require("tslib"); var tslib_1 = require("tslib");
var assert_1 = tslib_1.__importDefault(require("assert")); var assert_1 = tslib_1.__importDefault(require("assert"));
var filter_1 = require("../store/filter"); var filter_1 = require("../store/filter");
@ -123,6 +123,7 @@ function translateCheckerInAsyncContext(checker) {
if (context.isRoot()) { if (context.isRoot()) {
return [2 /*return*/, 0]; return [2 /*return*/, 0];
} }
(0, assert_1.default)(operation.action !== 'create', "".concat(entity, "\u4E0A\u7684create\u52A8\u4F5C\u5B9A\u4E49\u4E86relation\u7C7B\u578B\u7684checker,\u8BF7\u4F7F\u7528expressionRelation\u66FF\u4EE3"));
// 对后台而言将生成的relationFilter加到filter之上(select可以在此加以权限的过滤) // 对后台而言将生成的relationFilter加到filter之上(select可以在此加以权限的过滤)
_b = operation; _b = operation;
_c = filter_1.combineFilters; _c = filter_1.combineFilters;
@ -143,43 +144,32 @@ function translateCheckerInAsyncContext(checker) {
return (function (_a, context, option) { return (function (_a, context, option) {
var operation = _a.operation; var operation = _a.operation;
return tslib_1.__awaiter(_this, void 0, void 0, function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var exprResult, result2, isLegal; var exprResult, expressionEntity, expr, expressionFilter, _b, result, isLegal;
var _this = this; return tslib_1.__generator(this, function (_c) {
return tslib_1.__generator(this, function (_b) { switch (_c.label) {
switch (_b.label) {
case 0: case 0:
if (context.isRoot() && type === 'expressionRelation') { if (context.isRoot() && type === 'expressionRelation') {
return [2 /*return*/, 0]; return [2 /*return*/, 0];
} }
return [4 /*yield*/, expression_1(operation, context, option)]; return [4 /*yield*/, expression_1(operation, context, option)];
case 1: case 1:
exprResult = _b.sent(); exprResult = _c.sent();
if (!(typeof exprResult === 'string')) return [3 /*break*/, 2]; if (!(typeof exprResult === 'string')) return [3 /*break*/, 2];
throw new Exception_1.OakUserUnpermittedException(exprResult || errMsg_2); throw new Exception_1.OakUserUnpermittedException(exprResult || errMsg_2);
case 2: case 2:
if (!(exprResult === undefined)) return [3 /*break*/, 3]; if (!(exprResult === undefined)) return [3 /*break*/, 3];
return [2 /*return*/, 0]; return [2 /*return*/, 0];
case 3: return [4 /*yield*/, Promise.all(exprResult.map(function (e1) { return Promise.all(e1.map(function (e2) { return tslib_1.__awaiter(_this, void 0, void 0, function () { case 3:
var expressionEntity, expr, expressionFilter, _a, result; expressionEntity = exprResult.entity, expr = exprResult.expr, expressionFilter = exprResult.filter;
return tslib_1.__generator(this, function (_b) { return [4 /*yield*/, context.select(expressionEntity, {
switch (_b.label) { data: {
case 0: $expr: expr,
expressionEntity = e2.entity, expr = e2.expr, expressionFilter = e2.filter; },
return [4 /*yield*/, context.select(expressionEntity, { filter: expressionFilter,
data: { }, Object.assign({}, option, { dontCollect: true }))];
$expr: expr,
},
filter: expressionFilter,
}, Object.assign({}, option, { dontCollect: true }))];
case 1:
_a = tslib_1.__read.apply(void 0, [_b.sent(), 1]), result = _a[0];
return [2 /*return*/, result ? result.$expr : false];
}
});
}); })); }))];
case 4: case 4:
result2 = _b.sent(); _b = tslib_1.__read.apply(void 0, [_c.sent(), 1]), result = _b[0];
isLegal = result2.find(function (r1) { return r1.every(function (r2) { return r2 === true; }); }); isLegal = result ? result.$expr : false;
if (!isLegal) { if (!isLegal) {
// 条件判定为假,抛异常 // 条件判定为假,抛异常
if (type === 'expression') { if (type === 'expression') {
@ -189,7 +179,7 @@ function translateCheckerInAsyncContext(checker) {
throw new Exception_1.OakUserUnpermittedException(errMsg_2); throw new Exception_1.OakUserUnpermittedException(errMsg_2);
} }
} }
_b.label = 5; _c.label = 5;
case 5: return [2 /*return*/, 0]; case 5: return [2 /*return*/, 0];
} }
}); });
@ -260,18 +250,14 @@ function translateCheckerInSyncContext(checker) {
} }
else { else {
(0, assert_1.default)(!(exprResult instanceof Promise)); (0, assert_1.default)(!(exprResult instanceof Promise));
var result2 = exprResult.map(function (e1) { return e1.map(function (e2) { var expressionEntity = exprResult.entity, expr = exprResult.expr, expressionFilter = exprResult.filter;
var expressionEntity = e2.entity, expr = e2.expr, expressionFilter = e2.filter; var _a = tslib_1.__read(context.select(expressionEntity, {
var _a = tslib_1.__read(context.select(expressionEntity, { data: {
data: { $expr: expr,
$expr: expr, },
}, filter: expressionFilter,
filter: expressionFilter, }, Object.assign({}, option, { dontCollect: true })), 1), result = _a[0];
}, Object.assign({}, option, { dontCollect: true })), 1), result = _a[0]; var isLegal = result ? result.$expr : false;
return result ? result.$expr : false;
}); });
// exprResult外层是or里层是and关系
var isLegal = result2.find(function (r1) { return r1.every(function (r2) { return r2 === true; }); });
if (!isLegal) { if (!isLegal) {
// 条件判定为假,抛异常 // 条件判定为假,抛异常
if (type === 'expression') { if (type === 'expression') {
@ -315,247 +301,230 @@ function buildReverseHierarchyMap(relationHierarchy) {
} }
return reverseHierarchy; return reverseHierarchy;
} }
function translateSingleCascadeRelationItem(schema, lch, entity2, entityId, userId) { function translateCascadeRelationFilterMaker(schema, lch, entity2) {
var cascadePath = lch.cascadePath, relations = lch.relations; var cascadePath = lch.cascadePath, relations = lch.relations;
var paths = cascadePath.split('.'); var paths = cascadePath.split('.');
var translateFilterIter = function (entity, iter) { var translateRelationFilter = function (entity) {
var _a, _b, _c, _d, _e; // 有两种情况此entity和user有Relation定义或是此entity上有userId
if (schema[entity].relation) {
var relationEntityName_1 = "user".concat((0, string_1.firstLetterUpperCase)(entity));
return function (userId) {
var _a;
var filter = relations ? {
userId: userId,
relation: {
$in: relations,
},
} : {
userId: userId,
};
return {
id: {
$in: {
entity: relationEntityName_1,
data: (_a = {},
_a["".concat(entity, "Id")] = 1,
_a),
filter: filter,
},
},
};
};
}
var attributes = schema[entity].attributes;
(0, assert_1.default)(attributes.hasOwnProperty('userId') && attributes.userId.type === 'ref' && attributes.userId.ref === 'user', "\u5728".concat(entity, "\u4E0A\u65E2\u627E\u4E0D\u5230userId\uFF0C\u4E5F\u6CA1\u6709relation\u5B9A\u4E49"));
return function (userId) { return ({
userId: userId,
}); };
};
var translateFilterMakerIter = function (entity, iter) {
var relation = (0, relation_1.judgeRelation)(schema, entity, paths[iter]); var relation = (0, relation_1.judgeRelation)(schema, entity, paths[iter]);
if (iter === paths.length - 1) { if (iter === paths.length - 1) {
if (relation === 2) { if (relation === 2) {
return { var filterMaker_1 = translateRelationFilter(paths[iter]);
entity: paths[iter], return function (userId) {
entityId: { var _a;
$in: { var filter = filterMaker_1(userId);
entity: "user".concat((0, string_1.firstLetterUpperCase)(paths[iter])), if (filter.$in) {
data: (_a = {}, return {
_a["".concat(paths[iter], "Id")] = 1, entity: paths[iter],
_a), entityId: filter
filter: { };
userId: userId,
relation: {
$in: relations,
},
},
},
} }
return _a = {},
_a[paths[iter]] = filter,
_a;
}; };
} }
(0, assert_1.default)(typeof relation === 'string'); (0, assert_1.default)(typeof relation === 'string');
return _b = {}, var filterMaker_2 = translateRelationFilter(relation);
_b["".concat(paths[iter], "Id")] = { return function (userId) {
$in: { var _a, _b;
entity: "user".concat((0, string_1.firstLetterUpperCase)(relation)), var filter = filterMaker_2(userId);
data: (_c = {}, if (filter.$in) {
_c["".concat(relation, "Id")] = 1, return _a = {},
_c), _a["".concat(paths[iter], "Id")] = filter,
filter: { _a;
userId: userId, }
relation: { return _b = {},
$in: relations, _b[paths[iter]] = filter,
}, _b;
}, };
},
},
_b;
} }
else { else {
var subFilter = translateFilterIter(paths[iter], iter + 1); var subFilterMaker_1 = translateFilterMakerIter(paths[iter], iter + 1);
if (iter === 0) { if (iter === 0) {
return _d = {}, return function (userId) {
_d[paths[iter]] = subFilter, var _a;
_d.id = entityId, var subFilter = subFilterMaker_1(userId);
_d; return _a = {},
_a[paths[iter]] = subFilter,
_a;
};
} }
return _e = {}, return function (userId) {
_e[paths[iter]] = subFilter, var _a;
_e; return (_a = {},
_a[paths[iter]] = subFilterMaker_1(userId),
_a);
};
} }
}; };
var filter = translateFilterIter(entity2, 0); var filter = paths.length > 0 ? translateFilterMakerIter(entity2, 0) : translateRelationFilter(entity2);
return { return filter;
entity: entity2,
filter: filter,
expr: {
$gt: [{
'#attr': '$$createAt$$',
}, 0]
},
};
} }
function translateFromCascadeRelationHierarchy(schema, legalCascadeHierarchies, entity, entityId, userId) { function translateActionAuthFilterMaker(schema, relationItem, entity) {
if (legalCascadeHierarchies instanceof Array) { if (relationItem instanceof Array) {
return legalCascadeHierarchies.map(function (ele) { var maker_1 = relationItem.map(function (ele) {
if (ele instanceof Array) { if (ele instanceof Array) {
return ele.map(function (ele2) { return translateSingleCascadeRelationItem(schema, ele2, entity, entityId, userId); }); return ele.map(function (ele2) { return translateCascadeRelationFilterMaker(schema, ele2, entity); });
} }
return [translateSingleCascadeRelationItem(schema, ele, entity, entityId, userId)]; return [translateCascadeRelationFilterMaker(schema, ele, entity)];
}); });
return function (userId) { return ({
$or: maker_1.map(function (ele) { return ({
$and: ele.map(function (ele2) { return ele2(userId); })
}); })
}); };
} }
else { var filterMaker = translateCascadeRelationFilterMaker(schema, relationItem, entity);
return [[translateSingleCascadeRelationItem(schema, legalCascadeHierarchies, entity, entityId, userId)]]; return function (userId) { return filterMaker(userId); };
}
} }
function makeRelationExpressionCombination(schema, entity, entityId, userId, relation, reverseHierarchy, reverseCascadeRelationHierarchy) { function createAuthCheckers(schema, authDict) {
var _a;
var userEntityName = "user".concat((0, string_1.firstLetterUpperCase)(entity));
var entityIdAttr = "".concat(entity, "Id");
var legalRelations = reverseHierarchy && reverseHierarchy[relation];
var legalCascadeHierarchies = reverseCascadeRelationHierarchy && reverseCascadeRelationHierarchy[relation];
if (!legalRelations && !legalCascadeHierarchies) {
return undefined;
}
if ((legalRelations === null || legalRelations === void 0 ? void 0 : legalRelations.length) === 0) {
throw new Error('这是不应该跑出来的情况,请杀程序员祭天');
}
var expressionCombination = [];
if (legalRelations && legalRelations.length > 0) {
expressionCombination.push([{
entity: userEntityName,
expr: {
$gt: [{
'#attr': '$$createAt$$',
}, 0]
},
filter: (_a = {
userId: userId
},
_a[entityIdAttr] = entityId,
_a.relation = {
$in: legalRelations,
},
_a)
}]);
}
if (legalCascadeHierarchies) {
expressionCombination.push.apply(expressionCombination, tslib_1.__spreadArray([], tslib_1.__read(translateFromCascadeRelationHierarchy(schema, legalCascadeHierarchies, entity, entityId, userId)), false));
}
return expressionCombination;
}
function createRelationHierarchyCheckers(schema) {
var checkers = []; var checkers = [];
var _loop_1 = function (entity) { var _loop_1 = function (entity) {
var _a = schema[entity], relationHierarchy = _a.relationHierarchy, reverseCascadeRelationHierarchy = _a.reverseCascadeRelationHierarchy; var _a, _b;
if (relationHierarchy || reverseCascadeRelationHierarchy) { if (authDict[entity]) {
var reverseHierarchy_1 = relationHierarchy && buildReverseHierarchyMap(relationHierarchy); var _c = authDict[entity], relationAuth = _c.relationAuth, actionAuth = _c.actionAuth;
var userEntityName_1 = "user".concat((0, string_1.firstLetterUpperCase)(entity)); if (relationAuth) {
var entityIdAttr_1 = "".concat(entity, "Id"); var raFilterMakerDict_1 = {};
checkers.push({ for (var r in relationAuth) {
entity: userEntityName_1, Object.assign(raFilterMakerDict_1, (_a = {},
action: 'create', _a[r] = translateActionAuthFilterMaker(schema, relationAuth[r], entity),
type: 'expressionRelation', _a));
expression: function (operation, context) {
var data = operation.data;
(0, assert_1.default)(!(data instanceof Array));
var _a = data, relation = _a.relation, _b = entityIdAttr_1, entityId = _a[_b];
var userId = context.getCurrentUserId();
var schema = context.getSchema();
return makeRelationExpressionCombination(schema, entity, entityId, userId, relation, reverseHierarchy_1, reverseCascadeRelationHierarchy);
},
errMsg: '越权操作',
});
checkers.push({
entity: userEntityName_1,
action: 'remove',
type: 'expressionRelation',
expression: function (operation, context) {
var _a;
var userId = context.getCurrentUserId();
var filter = operation.filter;
var makeFilterFromRows = function (rows) {
var relations = (0, lodash_1.uniq)(rows.map(function (ele) { return ele.relation; }));
var entityIds = (0, lodash_1.uniq)(rows.map(function (ele) { return ele[entityIdAttr_1]; }));
(0, assert_1.default)(entityIds.length === 1, "\u5728\u56DE\u6536".concat(userEntityName_1, "\u4E0A\u6743\u9650\u65F6\uFF0C\u5355\u6B21\u56DE\u6536\u6D89\u53CA\u5230\u4E86\u4E0D\u540C\u7684\u5BF9\u8C61\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u88AB\u5141\u8BB8"));
var entityId = entityIds[0];
var schema = context.getSchema();
var exprComb = relations.map(function (relation) { return makeRelationExpressionCombination(schema, entity, entityId, userId, relation, reverseHierarchy_1, reverseCascadeRelationHierarchy); });
// 对每个relation求出其相应的exprComb此操作对多行进行expr需要对之进行类似于笛卡尔积的相乘
var result = exprComb.reduce(function (accu, current) {
var e_2, _a, e_3, _b;
if (!current) {
return accu;
}
var result2 = [];
try {
for (var current_1 = (e_2 = void 0, tslib_1.__values(current)), current_1_1 = current_1.next(); !current_1_1.done; current_1_1 = current_1.next()) {
var c = current_1_1.value;
try {
for (var _c = (e_3 = void 0, tslib_1.__values(accu)), _d = _c.next(); !_d.done; _d = _c.next()) {
var a = _d.value;
result2.push(a.concat(c));
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
}
finally { if (e_3) throw e_3.error; }
}
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (current_1_1 && !current_1_1.done && (_a = current_1.return)) _a.call(current_1);
}
finally { if (e_2) throw e_2.error; }
}
return result2;
}, [[]]);
return result && result.length > 0 ? result : undefined;
};
var toBeRemoved = context.select(userEntityName_1, {
data: (_a = {
id: 1,
relation: 1
},
_a[entityIdAttr_1] = 1,
_a),
filter: filter,
}, { dontCollect: true });
if (toBeRemoved instanceof Promise) {
return toBeRemoved.then(function (rows) { return makeFilterFromRows(rows); });
}
return makeFilterFromRows(toBeRemoved);
},
errMsg: '越权操作',
});
/* // 一个人不能授权给自己也不能删除自己的授权
checkers.push({
entity: userEntityName as keyof ED,
action: 'create' as ED[keyof ED]['Action'],
type: 'data',
checker: (data, context) => {
assert(!(data instanceof Array));
const { userId } = data as ED[keyof ED]['CreateSingle']['data'];
const userId2 = context.getCurrentUserId(true);
if (userId === userId2) {
throw new OakDataException('不允许授权给自己');
}
} }
}); var userEntityName_1 = "user".concat((0, string_1.firstLetterUpperCase)(entity));
var entityIdAttr_1 = "".concat(entity, "Id");
checkers.push({ checkers.push({
entity: userEntityName as keyof ED, entity: userEntityName_1,
action: 'remove' as ED[keyof ED]['Action'], action: 'create',
type: 'row', type: 'expressionRelation',
filter: (operation, context) => { expression: function (operation, context) {
const userId = context.getCurrentUserId(true); var data = operation.data;
if (userId) { (0, assert_1.default)(!(data instanceof Array));
var _a = data, relation = _a.relation, _b = entityIdAttr_1, entityId = _a[_b];
var userId = context.getCurrentUserId();
if (!raFilterMakerDict_1[relation]) {
return;
}
var filter = raFilterMakerDict_1[relation](userId);
return { return {
userId: { entity: entity,
$ne: userId, filter: filter,
expr: {
$gt: [{
'#attr': '$$createAt$$',
}, 0]
}, },
}; };
},
errMsg: '越权操作',
});
checkers.push({
entity: userEntityName_1,
action: 'remove',
type: 'relation',
relationFilter: function (operation, context) {
var _a;
var userId = context.getCurrentUserId();
var filter = operation.filter;
var makeFilterFromRows = function (rows) {
var relations = (0, lodash_1.uniq)(rows.map(function (ele) { return ele.relation; }));
var entityIds = (0, lodash_1.uniq)(rows.map(function (ele) { return ele[entityIdAttr_1]; }));
(0, assert_1.default)(entityIds.length === 1, "\u5728\u56DE\u6536".concat(userEntityName_1, "\u4E0A\u6743\u9650\u65F6\uFF0C\u5355\u6B21\u56DE\u6536\u6D89\u53CA\u5230\u4E86\u4E0D\u540C\u7684\u5BF9\u8C61\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u88AB\u5141\u8BB8"));
var entityId = entityIds[0];
// 所有的relation条件要同时满足and关系注意这里的filter翻译出来是在entity对象上不是在userEntity对象上
return {
$and: relations.map(function (relation) { return raFilterMakerDict_1[relation]; }).filter(function (ele) { return !!ele; }).map(function (ele) {
var _a;
return (_a = {},
_a[entity] = ele(userId),
_a);
})
};
};
var toBeRemoved = context.select(userEntityName_1, {
data: (_a = {
id: 1,
relation: 1
},
_a[entityIdAttr_1] = 1,
_a),
filter: filter,
}, { dontCollect: true });
if (toBeRemoved instanceof Promise) {
return toBeRemoved.then(function (rows) { return makeFilterFromRows(rows); });
}
return makeFilterFromRows(toBeRemoved);
},
errMsg: '越权操作',
});
// 转让权限现在用update动作只允许update userId给其它人
// todo 等实现的时候再写
}
if (actionAuth) {
var aaFilterMakerDict = {};
for (var a in actionAuth) {
Object.assign(aaFilterMakerDict, (_b = {},
_b[a] = translateActionAuthFilterMaker(schema, actionAuth[a], entity),
_b));
if (a === 'create') {
/**
* create动作所增加的auth约束只可能在外键的对象上所以需要对此外键对象进行查找
*/
var _d = actionAuth[a];
/* checkers.push({
entity,
action: a,
type: 'expressionRelation',
expression: (operation, context) => {
// create要保证数据的外键指向关系满足filter的约束因为这行还没有插入所以要
}
}); */
} }
console.warn(`没有当前用户但在删除权限,请检查。对象是${entity}`); else {
return {}; /* checkers.push({
}, entity,
errMsg: '不允许回收自己的授权', action: a,
}); */ type: 'relation',
// 转让权限现在用update动作只允许update userId给其它人 relationFilter: (operation, context) => {
// todo 等实现的时候再写 const { filter } = operation;
}
}); */
}
}
}
} }
}; };
for (var entity in schema) { for (var entity in schema) {
@ -563,4 +532,4 @@ function createRelationHierarchyCheckers(schema) {
} }
return checkers; return checkers;
} }
exports.createRelationHierarchyCheckers = createRelationHierarchyCheckers; exports.createAuthCheckers = createAuthCheckers;

View File

@ -1,4 +1,4 @@
import { EntityDict } from "./Entity"; import { CascadeRelationItem, EntityDict } from "./Entity";
import { GenericAction } from '../actions/action'; import { GenericAction } from '../actions/action';
export declare type Action = string; export declare type Action = string;
export declare type State = string; export declare type State = string;
@ -13,10 +13,6 @@ export declare type ActionDictOfEntityDict<E extends EntityDict> = {
[A in keyof E[T]['OpSchema']]?: ActionDef<string, string>; [A in keyof E[T]['OpSchema']]?: ActionDef<string, string>;
}; };
}; };
export declare type CascadeActionItem = {
cascadePath: string;
relation: string[];
};
export declare type CascadeActionAuth<A extends Action = ''> = { export declare type CascadeActionAuth<A extends Action = ''> = {
[K in A | GenericAction]?: CascadeActionItem | (CascadeActionItem | CascadeActionItem[])[]; [K in A | GenericAction]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[];
}; };

10
lib/types/Auth.d.ts vendored
View File

@ -1,3 +1,4 @@
import { CascadeActionAuth, CascadeRelationAuth } from ".";
import { AsyncContext } from "../store/AsyncRowStore"; import { AsyncContext } from "../store/AsyncRowStore";
import { SyncContext } from "../store/SyncRowStore"; import { SyncContext } from "../store/SyncRowStore";
import { EntityDict, OperateOption, SelectOption } from "../types/Entity"; import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
@ -42,7 +43,7 @@ export declare type ExpressionTask<ED extends EntityDict, T extends keyof ED> =
expr: RefOrExpression<keyof ED[T]['OpSchema']>; expr: RefOrExpression<keyof ED[T]['OpSchema']>;
filter: ED[T]['Selection']['filter']; filter: ED[T]['Selection']['filter'];
}; };
export declare type ExpressionTaskCombination<ED extends EntityDict> = ExpressionTask<ED, keyof ED>[][]; export declare type ExpressionTaskCombination<ED extends EntityDict> = ExpressionTask<ED, keyof ED>;
export declare type ExpressionChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = { export declare type ExpressionChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
priority?: number; priority?: number;
type: 'expression'; type: 'expression';
@ -62,3 +63,10 @@ export declare type ExpressionRelationChecker<ED extends EntityDict, T extends k
conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']); conditionalFilter?: ED[T]['Update']['filter'] | ((operation: ED[T]['Operation'], context: Cxt, option: OperateOption) => ED[T]['Update']['filter']);
}; };
export declare type Checker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = DataChecker<ED, T, Cxt> | RowChecker<ED, T, Cxt> | RelationChecker<ED, T, Cxt> | ExpressionChecker<ED, T, Cxt> | ExpressionRelationChecker<ED, T, Cxt>; export declare type Checker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = DataChecker<ED, T, Cxt> | RowChecker<ED, T, Cxt> | RelationChecker<ED, T, Cxt> | ExpressionChecker<ED, T, Cxt> | ExpressionRelationChecker<ED, T, Cxt>;
export declare type AuthDef<ED extends EntityDict, T extends keyof ED> = {
relationAuth?: CascadeRelationAuth<NonNullable<ED[T]['Relation']>>;
actionAuth?: CascadeActionAuth<ED[T]['Action']>;
};
export declare type AuthDefDict<ED extends EntityDict> = {
[K in keyof ED]?: AuthDef<ED, K>;
};

View File

@ -80,6 +80,7 @@ export interface EntityDef {
CreateMulti: DeduceCreateMultipleOperation<this['Schema']>; CreateMulti: DeduceCreateMultipleOperation<this['Schema']>;
Update: DeduceUpdateOperation<this['Schema']>; Update: DeduceUpdateOperation<this['Schema']>;
Remove: DeduceRemoveOperation<this['Schema']>; Remove: DeduceRemoveOperation<this['Schema']>;
Relation?: string;
} }
export interface EntityDict { export interface EntityDict {
[E: string]: EntityDef; [E: string]: EntityDef;
@ -104,7 +105,7 @@ export declare type AggregationResult<SH extends GeneralEntityShape> = Array<{
'#data'?: Partial<SH>; '#data'?: Partial<SH>;
}>; }>;
export declare type AttrFilter<SH extends GeneralEntityShape> = { export declare type AttrFilter<SH extends GeneralEntityShape> = {
[K in keyof SH]: any; [K in keyof SH]?: any;
}; };
export declare type DeduceFilter<SH extends GeneralEntityShape> = MakeFilter<AttrFilter<SH> & ExprOp<keyof SH>>; export declare type DeduceFilter<SH extends GeneralEntityShape> = MakeFilter<AttrFilter<SH> & ExprOp<keyof SH>>;
export declare type DeduceSorterAttr<SH extends GeneralEntityShape> = OneOf<{ export declare type DeduceSorterAttr<SH extends GeneralEntityShape> = OneOf<{
@ -155,9 +156,9 @@ export declare type RelationHierarchy<R extends string> = {
}; };
export declare type CascadeRelationItem = { export declare type CascadeRelationItem = {
cascadePath: string; cascadePath: string;
relations: string[]; relations?: string[];
}; };
export declare type ReverseCascadeRelationHierarchy<R extends string> = { export declare type CascadeRelationAuth<R extends string> = {
[K in R]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[]; [K in R]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[];
}; };
export declare type SelectOpResult<ED extends EntityDict> = { export declare type SelectOpResult<ED extends EntityDict> = {

View File

@ -1,5 +1,5 @@
import { ActionType } from '.'; import { ActionType } from '.';
import { EntityDict, EntityShape, InstinctiveAttributes, RelationHierarchy, ReverseCascadeRelationHierarchy } from './Entity'; import { EntityDict, EntityShape, InstinctiveAttributes } from './Entity';
import { DataType, DataTypeParams } from './schema/DataTypes'; import { DataType, DataTypeParams } from './schema/DataTypes';
export declare type Ref = 'ref'; export declare type Ref = 'ref';
export interface Column<SH extends EntityShape> { export interface Column<SH extends EntityShape> {
@ -36,7 +36,7 @@ export declare type UniqConstraint<SH extends EntityShape> = {
attributes: Array<keyof SH>; attributes: Array<keyof SH>;
type?: string; type?: string;
}; };
export interface StorageDesc<SH extends EntityShape, Relation extends string = ''> { export interface StorageDesc<SH extends EntityShape> {
storageName?: string; storageName?: string;
comment?: string; comment?: string;
attributes: Attributes<SH>; attributes: Attributes<SH>;
@ -48,10 +48,9 @@ export interface StorageDesc<SH extends EntityShape, Relation extends string = '
static?: true; static?: true;
actions: string[]; actions: string[];
actionType: ActionType; actionType: ActionType;
relationHierarchy?: RelationHierarchy<Relation>; relation?: string[];
reverseCascadeRelationHierarchy?: ReverseCascadeRelationHierarchy<Relation>;
view?: true; view?: true;
} }
export declare type StorageSchema<ED extends EntityDict> = { export declare type StorageSchema<ED extends EntityDict> = {
[K in keyof ED]: StorageDesc<ED[K]['OpSchema'], any>; [K in keyof ED]: StorageDesc<ED[K]['OpSchema']>;
}; };

View File

@ -1,13 +1,15 @@
import { EntityDict } from '../base-app-domain'; import { EntityDict } from '../base-app-domain';
import { AsyncContext } from '../store/AsyncRowStore'; import { AsyncContext } from '../store/AsyncRowStore';
import { createRelationHierarchyCheckers } from '../store/checker'; import { createAuthCheckers } from '../store/checker';
import { createModiRelatedCheckers } from '../store/modi'; import { createModiRelatedCheckers } from '../store/modi';
import { SyncContext } from '../store/SyncRowStore'; import { SyncContext } from '../store/SyncRowStore';
import { StorageSchema, EntityDict as BaseEntityDict, Checker } from '../types'; import { StorageSchema, EntityDict as BaseEntityDict, Checker, AuthDef, AuthDefDict } from '../types';
export function createDynamicCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>){ export function createDynamicCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict?: AuthDefDict<ED>){
const checkers: Checker<ED, keyof ED, Cxt>[] = []; const checkers: Checker<ED, keyof ED, Cxt>[] = [];
checkers.push(...createModiRelatedCheckers<ED, Cxt>(schema)); checkers.push(...createModiRelatedCheckers<ED, Cxt>(schema));
checkers.push(...createRelationHierarchyCheckers<ED, Cxt>(schema)); if (authDict) {
checkers.push(...createAuthCheckers<ED, Cxt>(schema, authDict));
}
return checkers; return checkers;
} }

View File

@ -25,8 +25,8 @@ const Schema: Record<string, {
states: string[]; states: string[];
sourceFile: ts.SourceFile; sourceFile: ts.SourceFile;
locale: ts.ObjectLiteralExpression; locale: ts.ObjectLiteralExpression;
relationHierarchy?: ts.ObjectLiteralExpression; // relationHierarchy?: ts.ObjectLiteralExpression;
reverseCascadeRelationHierarchy?: ts.ObjectLiteralExpression; // reverseCascadeRelationHierarchy?: ts.ObjectLiteralExpression;
toModi: boolean; toModi: boolean;
actionType: string; actionType: string;
static: boolean; static: boolean;
@ -498,8 +498,8 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
const localEnumStringTypes: string[] = []; const localEnumStringTypes: string[] = [];
const additionalImports: ts.ImportDeclaration[] = []; const additionalImports: ts.ImportDeclaration[] = [];
let localeDef: ts.ObjectLiteralExpression | undefined = undefined; let localeDef: ts.ObjectLiteralExpression | undefined = undefined;
let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined; // let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
let reverseCascadeRelationHierarchy: ts.ObjectLiteralExpression | undefined = undefined; // let reverseCascadeRelationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
ts.forEachChild(sourceFile!, (node) => { ts.forEachChild(sourceFile!, (node) => {
if (ts.isImportDeclaration(node)) { if (ts.isImportDeclaration(node)) {
const entityImported = getEntityImported(node); const entityImported = getEntityImported(node);
@ -1125,7 +1125,7 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
_static = true; // static如果有值只能为true _static = true; // static如果有值只能为true
} }
} }
else if (ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'RelationHierarchy') { /* else if (ts.isTypeReferenceNode(declaration.type!) && ts.isIdentifier(declaration.type.typeName) && declaration.type.typeName.text === 'RelationHierarchy') {
// RelationHierary // RelationHierary
assert(hasRelationDef, `${moduleName}中的Relation定义在RelationHierarchy之后`); assert(hasRelationDef, `${moduleName}中的Relation定义在RelationHierarchy之后`);
const { initializer } = declaration; const { initializer } = declaration;
@ -1138,9 +1138,9 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
const { initializer } = declaration; const { initializer } = declaration;
assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`); assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`);
reverseCascadeRelationHierarchy = initializer; reverseCascadeRelationHierarchy = initializer;
} } */
else { else {
throw new Error(`${moduleName}:不能理解的定义内容${declaration.name.getText()}`); throw new Error(`${moduleName}:不能理解的定义内容${(declaration.name as ts.Identifier).text}`);
} }
} }
); );
@ -1182,7 +1182,7 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
locale: localeDef, locale: localeDef,
}); });
} }
if (hasRelationDef) { /* if (hasRelationDef) {
if(!relationHierarchy && !reverseCascadeRelationHierarchy){ if(!relationHierarchy && !reverseCascadeRelationHierarchy){
console.warn(`${filename}中定义了Relation,但并没有relationHierarchy或reverseCascadeRelationHierarchy的定义请注意自主编写权限分配的checker`); console.warn(`${filename}中定义了Relation,但并没有relationHierarchy或reverseCascadeRelationHierarchy的定义请注意自主编写权限分配的checker`);
} }
@ -1200,7 +1200,7 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
else { else {
assert(!relationHierarchy, `${filename}中具有relationHierarchy定义但没有Relation定义`); assert(!relationHierarchy, `${filename}中具有relationHierarchy定义但没有Relation定义`);
assert(!reverseCascadeRelationHierarchy, `${filename}中具有reverseCascadeRelationHierarchy定义但没有Relation定义`) assert(!reverseCascadeRelationHierarchy, `${filename}中具有reverseCascadeRelationHierarchy定义但没有Relation定义`)
} } */
assign(Schema, { assign(Schema, {
[moduleName]: schema, [moduleName]: schema,
@ -5664,7 +5664,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
for (const entity in Schema) { for (const entity in Schema) {
const indexExpressions: ts.Expression[] = []; const indexExpressions: ts.Expression[] = [];
const { sourceFile, inModi, indexes, toModi, actionType, static: _static, relationHierarchy, reverseCascadeRelationHierarchy } = Schema[entity]; const { sourceFile, inModi, indexes, toModi, actionType, static: _static, hasRelationDef } = Schema[entity];
const fromSchemaSpecifiers = [ const fromSchemaSpecifiers = [
factory.createImportSpecifier( factory.createImportSpecifier(
false, false,
@ -5672,7 +5672,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
factory.createIdentifier("OpSchema") factory.createIdentifier("OpSchema")
) )
]; ];
if (relationHierarchy || reverseCascadeRelationHierarchy) { /* if (relationHierarchy || reverseCascadeRelationHierarchy) {
fromSchemaSpecifiers.push( fromSchemaSpecifiers.push(
factory.createImportSpecifier( factory.createImportSpecifier(
false, false,
@ -5680,7 +5680,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
factory.createIdentifier("Relation") factory.createIdentifier("Relation")
) )
); );
} } */
const statements: ts.Statement[] = [ const statements: ts.Statement[] = [
factory.createImportDeclaration( factory.createImportDeclaration(
undefined, undefined,
@ -5881,7 +5881,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
) )
); );
} }
if (relationHierarchy) { /* if (relationHierarchy) {
propertyAssignments.push( propertyAssignments.push(
factory.createPropertyAssignment( factory.createPropertyAssignment(
factory.createIdentifier("relationHierarchy"), factory.createIdentifier("relationHierarchy"),
@ -5896,21 +5896,32 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
reverseCascadeRelationHierarchy, reverseCascadeRelationHierarchy,
) )
); );
} } */
if (hasRelationDef) {
const { type } = hasRelationDef;
assert(ts.isUnionTypeNode(type));
const { types } = type;
const relationTexts = types.map(
ele => {
assert(ts.isLiteralTypeNode(ele) && ts.isStringLiteral(ele.literal));
return ele.literal.text;
}
)
propertyAssignments.push(
factory.createPropertyAssignment(
factory.createIdentifier("relation"),
factory.createArrayLiteralExpression(relationTexts.map(
ele => factory.createStringLiteral(ele)
)),
)
);
}
const sdTypeArguments = [ const sdTypeArguments = [
factory.createTypeReferenceNode( factory.createTypeReferenceNode(
factory.createIdentifier("OpSchema"), factory.createIdentifier("OpSchema"),
undefined undefined
) )
]; ];
if (relationHierarchy) {
sdTypeArguments.push(
factory.createTypeReferenceNode(
factory.createIdentifier("Relation"),
undefined
)
)
}
statements.push( statements.push(
factory.createVariableStatement( factory.createVariableStatement(
[factory.createModifier(ts.SyntaxKind.ExportKeyword)], [factory.createModifier(ts.SyntaxKind.ExportKeyword)],

View File

@ -1,7 +1,7 @@
import assert from 'assert'; import assert from 'assert';
import { addFilterSegment, checkFilterContains, combineFilters } from "../store/filter"; import { addFilterSegment, checkFilterContains, combineFilters } from "../store/filter";
import { OakDataException, OakRowInconsistencyException, OakUserUnpermittedException } from '../types/Exception'; import { OakDataException, OakRowInconsistencyException, OakUserUnpermittedException } from '../types/Exception';
import { CascadeRelationItem, Checker, CreateTriggerInTxn, EntityDict, ExpressionRelationChecker, ExpressionTask, ExpressionTaskCombination, OperateOption, RefOrExpression, RelationHierarchy, ReverseCascadeRelationHierarchy, SelectOption, StorageSchema, Trigger, UpdateTriggerInTxn } from "../types"; import { AuthDef, AuthDefDict, CascadeRelationItem, Checker, CreateTriggerInTxn, EntityDict, ExpressionRelationChecker, ExpressionTask, ExpressionTaskCombination, OperateOption, RefOrExpression, RelationHierarchy, CascadeRelationAuth, SelectOption, StorageSchema, Trigger, UpdateTriggerInTxn } from "../types";
import { EntityDict as BaseEntityDict } from '../base-app-domain'; import { EntityDict as BaseEntityDict } from '../base-app-domain';
import { AsyncContext } from "./AsyncRowStore"; import { AsyncContext } from "./AsyncRowStore";
import { getFullProjection } from './actionDef'; import { getFullProjection } from './actionDef';
@ -90,6 +90,7 @@ export function translateCheckerInAsyncContext<
if (context.isRoot()) { if (context.isRoot()) {
return 0; return 0;
} }
assert(operation.action !== 'create', `${entity as string}上的create动作定义了relation类型的checker,请使用expressionRelation替代`);
// 对后台而言将生成的relationFilter加到filter之上(select可以在此加以权限的过滤) // 对后台而言将生成的relationFilter加到filter之上(select可以在此加以权限的过滤)
operation.filter = combineFilters([operation.filter, await relationFilter(operation, context, option)]); operation.filter = combineFilters([operation.filter, await relationFilter(operation, context, option)]);
return 0; return 0;
@ -110,30 +111,14 @@ export function translateCheckerInAsyncContext<
return 0; return 0;
} }
else { else {
const result2 = await Promise.all( const { entity: expressionEntity, expr, filter: expressionFilter } = exprResult;
exprResult.map( const [result] = await context.select(expressionEntity, {
(e1) => Promise.all( data: {
e1.map( $expr: expr,
async (e2) => { },
const { entity: expressionEntity, expr, filter: expressionFilter } = e2; filter: expressionFilter,
const [result] = await context.select(expressionEntity, { }, Object.assign({}, option, { dontCollect: true }));
data: { const isLegal = result ? result.$expr as boolean : false;
$expr: expr,
},
filter: expressionFilter,
}, Object.assign({}, option, { dontCollect: true }));
return result ? result.$expr as boolean : false;
}
)
)
)
);
// exprResult外层是or里层是and关系
const isLegal = result2.find(
(r1) => r1.every(
(r2) => r2 === true
)
);
if (!isLegal) { if (!isLegal) {
// 条件判定为假,抛异常 // 条件判定为假,抛异常
if (type === 'expression') { if (type === 'expression') {
@ -215,26 +200,14 @@ export function translateCheckerInSyncContext<
} }
else { else {
assert(!(exprResult instanceof Promise)); assert(!(exprResult instanceof Promise));
const result2 = exprResult.map( const { entity: expressionEntity, expr, filter: expressionFilter } = exprResult;
(e1) => e1.map( const [result] = context.select(expressionEntity, {
(e2) => { data: {
const { entity: expressionEntity, expr, filter: expressionFilter } = e2; $expr: expr,
const [result] = context.select(expressionEntity, { },
data: { filter: expressionFilter,
$expr: expr, }, Object.assign({}, option, { dontCollect: true }));
}, const isLegal = result ? result.$expr as boolean : false;
filter: expressionFilter,
}, Object.assign({}, option, { dontCollect: true }));
return result ? result.$expr as boolean : false;
}
)
);
// exprResult外层是or里层是and关系
const isLegal = result2.find(
(r1) => r1.every(
(r2) => r2 === true
)
);
if (!isLegal) { if (!isLegal) {
// 条件判定为假,抛异常 // 条件判定为假,抛异常
if (type === 'expression') { if (type === 'expression') {
@ -269,273 +242,255 @@ function buildReverseHierarchyMap(relationHierarchy: RelationHierarchy<any>) {
return reverseHierarchy; return reverseHierarchy;
} }
function translateSingleCascadeRelationItem<ED extends EntityDict & BaseEntityDict>( function translateCascadeRelationFilterMaker<ED extends EntityDict & BaseEntityDict>(
schema: StorageSchema<ED>, schema: StorageSchema<ED>,
lch: CascadeRelationItem, lch: CascadeRelationItem,
entity2: keyof ED, entity2: keyof ED): (userId: string) => ExpressionTask<ED, keyof ED>['filter'] {
entityId: string,
userId: string): ExpressionTask<ED, keyof ED> {
const { cascadePath, relations } = lch; const { cascadePath, relations } = lch;
const paths = cascadePath.split('.'); const paths = cascadePath.split('.');
const translateFilterIter = <T extends keyof ED>(entity: keyof ED, iter: number): ED[T]['Selection']['filter'] => { const translateRelationFilter = <T extends keyof ED>(entity: T): (userId: string) => ED[T]['Selection']['filter'] => {
const relation = judgeRelation(schema, entity, paths[iter]); // 有两种情况此entity和user有Relation定义或是此entity上有userId
if (iter === paths.length - 1) { if (schema[entity].relation) {
if (relation === 2) { const relationEntityName = `user${firstLetterUpperCase(entity as string)}`;
return { return (userId) => {
entity: paths[iter], const filter = relations ? {
entityId: { userId,
$in: { relation: {
entity: `user${firstLetterUpperCase(paths[iter])}`, $in: relations,
data: { },
[`${paths[iter]}Id`]: 1, } : {
}, userId,
filter: {
userId,
relation: {
$in: relations,
},
},
},
}
}; };
} return {
assert(typeof relation === 'string'); id: {
return { $in: {
[`${paths[iter]}Id`]: { entity: relationEntityName,
$in: { data: {
entity: `user${firstLetterUpperCase(relation)}`, [`${entity as string}Id`]: 1,
data: {
[`${relation}Id`]: 1,
},
filter: {
userId,
relation: {
$in: relations,
}, },
filter,
}, },
}, },
} }
}; };
} }
else {
const subFilter = translateFilterIter(paths[iter], iter + 1); const { attributes } = schema[entity];
if (iter === 0) { assert(attributes.hasOwnProperty('userId') && attributes.userId.type === 'ref' && attributes.userId.ref === 'user', `${entity as string}上既找不到userId也没有relation定义`);
return (userId) => ({
userId,
});
};
const translateFilterMakerIter = <T extends keyof ED>(entity: T, iter: number): (userId: string) => ED[T]['Selection']['filter'] => {
const relation = judgeRelation(schema, entity, paths[iter]);
if (iter === paths.length - 1) {
if (relation === 2) {
const filterMaker = translateRelationFilter(paths[iter]);
return (userId) => {
const filter = filterMaker(userId);
if (filter!.$in) {
return {
entity: paths[iter],
entityId: filter
};
}
return {
[paths[iter]]: filter,
};
}
}
assert(typeof relation === 'string');
const filterMaker = translateRelationFilter(relation);
return (userId) => {
const filter = filterMaker(userId);
if (filter!.$in) {
return {
[`${paths[iter]}Id`]: filter
};
}
return { return {
[paths[iter]]: subFilter, [paths[iter]]: filter,
id: entityId,
}; };
} }
return { }
[paths[iter]]: subFilter, else {
}; const subFilterMaker = translateFilterMakerIter(paths[iter], iter + 1);
if (iter === 0) {
return (userId) => {
const subFilter = subFilterMaker(userId);
return {
[paths[iter]]: subFilter,
};
};
}
return (userId) => ({
[paths[iter]]: subFilterMaker(userId),
});
} }
}; };
const filter = translateFilterIter(entity2, 0);
return { const filter = paths.length > 0 ? translateFilterMakerIter(entity2, 0) : translateRelationFilter(entity2);
entity: entity2, return filter;
filter,
expr: {
$gt: [{
'#attr': '$$createAt$$',
}, 0]
},
};
} }
function translateFromCascadeRelationHierarchy<ED extends EntityDict & BaseEntityDict>( function translateActionAuthFilterMaker<ED extends EntityDict & BaseEntityDict>(
schema: StorageSchema<ED>, schema: StorageSchema<ED>,
legalCascadeHierarchies: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[], relationItem: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[],
entity: keyof ED, entity: keyof ED
entityId: string, ): (userId: string) => ED[keyof ED]['Selection']['filter'] {
userId: string): ExpressionTaskCombination<ED> { if (relationItem instanceof Array) {
if (legalCascadeHierarchies instanceof Array) { const maker = relationItem.map(
return legalCascadeHierarchies.map(
ele => { ele => {
if (ele instanceof Array) { if (ele instanceof Array) {
return ele.map( return ele.map(
ele2 => translateSingleCascadeRelationItem(schema, ele2, entity, entityId, userId) ele2 => translateCascadeRelationFilterMaker(schema, ele2, entity)
); );
} }
return [translateSingleCascadeRelationItem(schema, ele, entity, entityId, userId)]; return [translateCascadeRelationFilterMaker(schema, ele, entity)];
} }
) );
} return (userId) => ({
else { $or: maker.map(
return [[translateSingleCascadeRelationItem(schema, legalCascadeHierarchies, entity, entityId, userId)]]; ele => ({
$and: ele.map(
ele2 => ele2(userId)!
)
})
)
})
} }
const filterMaker = translateCascadeRelationFilterMaker(schema, relationItem, entity);
return (userId) => filterMaker(userId);
} }
function makeRelationExpressionCombination<ED extends EntityDict & BaseEntityDict>( export function createAuthCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(
schema: StorageSchema<ED>, schema: StorageSchema<ED>,
entity: keyof ED, authDict: AuthDefDict<ED>) {
entityId: string,
userId: string,
relation: string,
reverseHierarchy?: Record<string, string[]>,
reverseCascadeRelationHierarchy?: ReverseCascadeRelationHierarchy<any>,
) {
const userEntityName = `user${firstLetterUpperCase(entity as string)}`;
const entityIdAttr = `${entity as string}Id`;
const legalRelations = reverseHierarchy && reverseHierarchy[relation];
const legalCascadeHierarchies = reverseCascadeRelationHierarchy && reverseCascadeRelationHierarchy[relation];
if (!legalRelations && !legalCascadeHierarchies) {
return undefined;
}
if (legalRelations?.length === 0) {
throw new Error('这是不应该跑出来的情况,请杀程序员祭天');
}
const expressionCombination: ExpressionTaskCombination<ED> = [];
if (legalRelations && legalRelations.length > 0) {
expressionCombination.push([{
entity: userEntityName as keyof ED,
expr: {
$gt: [{
'#attr': '$$createAt$$',
}, 0]
},
filter: {
userId,
[entityIdAttr]: entityId,
relation: {
$in: legalRelations,
}
}
}]);
}
if (legalCascadeHierarchies) {
expressionCombination.push(...translateFromCascadeRelationHierarchy<ED>(
schema,
legalCascadeHierarchies,
entity,
entityId,
userId!
));
}
return expressionCombination;
}
export function createRelationHierarchyCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>) {
const checkers: Checker<ED, keyof ED, Cxt>[] = []; const checkers: Checker<ED, keyof ED, Cxt>[] = [];
for (const entity in schema) { for (const entity in schema) {
const { relationHierarchy, reverseCascadeRelationHierarchy } = schema[entity]; if (authDict[entity]) {
if (relationHierarchy || reverseCascadeRelationHierarchy) { const { relationAuth, actionAuth } = authDict[entity]!;
const reverseHierarchy = relationHierarchy && buildReverseHierarchyMap(relationHierarchy); if (relationAuth) {
const userEntityName = `user${firstLetterUpperCase(entity)}`; const raFilterMakerDict = {} as Record<string, (userId: string) => ED[keyof ED]['Selection']['filter']>;
const entityIdAttr = `${entity}Id`; for (const r in relationAuth) {
checkers.push({ Object.assign(raFilterMakerDict, {
entity: userEntityName as keyof ED, [r]: translateActionAuthFilterMaker(schema, relationAuth[r as NonNullable<ED[keyof ED]['Relation']>]!, entity),
action: 'create', });
type: 'expressionRelation', }
expression: <T2 extends keyof ED>(operation: any, context: Cxt) => { const userEntityName = `user${firstLetterUpperCase(entity)}`;
const { data } = operation as ED[keyof ED]['Create']; const entityIdAttr = `${entity}Id`;
assert(!(data instanceof Array)); checkers.push({
const { relation, [entityIdAttr]: entityId } = data; entity: userEntityName as keyof ED,
const userId = context.getCurrentUserId(); action: 'create',
const schema = context.getSchema(); type: 'expressionRelation',
return makeRelationExpressionCombination(schema, entity, entityId, userId!, relation, reverseHierarchy, reverseCascadeRelationHierarchy); expression: (operation, context) => {
}, const { data } = operation as ED[keyof ED]['Create'];
errMsg: '越权操作', assert(!(data instanceof Array));
}); const { relation, [entityIdAttr]: entityId } = data;
checkers.push({ const userId = context.getCurrentUserId();
entity: userEntityName as keyof ED, if (!raFilterMakerDict[relation]) {
action: 'remove', return;
type: 'expressionRelation', }
expression: <T2 extends keyof ED>(operation: any, context: Cxt) => { const filter = raFilterMakerDict[relation]!(userId!);
const userId = context.getCurrentUserId(); return {
const { filter } = operation as ED[keyof ED]['Remove']; entity,
const makeFilterFromRows = (rows: Partial<ED[T2]['Schema']>[]) => { filter,
const relations = uniq(rows.map(ele => ele.relation)); expr: {
const entityIds = uniq(rows.map(ele => ele[entityIdAttr])); $gt: [{
assert(entityIds.length === 1, `在回收${userEntityName}上权限时,单次回收涉及到了不同的对象,此操作不被允许`); '#attr': '$$createAt$$',
const entityId = entityIds[0]!; }, 0]
const schema = context.getSchema();
const exprComb = relations.map(
(relation) => makeRelationExpressionCombination(
schema,
entity,
entityId,
userId!,
relation!,
reverseHierarchy,
reverseCascadeRelationHierarchy,
)
);
// 对每个relation求出其相应的exprComb此操作对多行进行expr需要对之进行类似于笛卡尔积的相乘
const result = exprComb.reduce(
(accu, current) => {
if (!current) {
return accu;
}
const result2 = [] as ExpressionTaskCombination<ED>;
for (const c of current) {
for (const a of accu!) {
result2.push(a.concat(c));
}
}
return result2;
}, },
[[]] as ExpressionTaskCombination<ED>, }
); },
errMsg: '越权操作',
});
return result && result.length > 0 ? result : undefined; checkers.push({
}; entity: userEntityName as keyof ED,
action: 'remove' as ED[keyof ED]['Action'],
type: 'relation',
relationFilter: (operation: any, context: Cxt) => {
const userId = context.getCurrentUserId();
const { filter } = operation as ED[keyof ED]['Remove'];
const makeFilterFromRows = (rows: Partial<ED[keyof ED]['Schema']>[]) => {
const relations = uniq(rows.map(ele => ele.relation));
const entityIds = uniq(rows.map(ele => ele[entityIdAttr]));
assert(entityIds.length === 1, `在回收${userEntityName}上权限时,单次回收涉及到了不同的对象,此操作不被允许`);
const entityId = entityIds[0]!;
const toBeRemoved = context.select(userEntityName, { // 所有的relation条件要同时满足and关系注意这里的filter翻译出来是在entity对象上不是在userEntity对象上
data: { return {
id: 1, $and: relations.map(
relation: 1, (relation) => raFilterMakerDict[relation!]
[entityIdAttr]: 1, ).filter(
}, ele => !!ele
filter, ).map(
}, { dontCollect: true }); ele => ({
if (toBeRemoved instanceof Promise) { [entity]: ele(userId!),
return toBeRemoved.then( })
(rows) => makeFilterFromRows(rows) )
); } as ED[keyof ED]['Selection']['filter'];
};
const toBeRemoved = context.select(userEntityName, {
data: {
id: 1,
relation: 1,
[entityIdAttr]: 1,
},
filter,
}, { dontCollect: true });
if (toBeRemoved instanceof Promise) {
return toBeRemoved.then(
(rows) => makeFilterFromRows(rows)
);
}
return makeFilterFromRows(toBeRemoved);
},
errMsg: '越权操作',
});
// 转让权限现在用update动作只允许update userId给其它人
// todo 等实现的时候再写
}
if (actionAuth) {
const aaFilterMakerDict = {} as Record<string, (suserId: string) => ED[keyof ED]['Selection']['filter']>;
for (const a in actionAuth) {
Object.assign(aaFilterMakerDict, {
[a]: translateActionAuthFilterMaker(schema, actionAuth[a as ED[keyof ED]['Action']]!, entity),
});
if (a === 'create') {
/**
* create动作所增加的auth约束只可能在外键的对象上
*/
const { } = actionAuth[a as ED[keyof ED]['Action']]!;
/* checkers.push({
entity,
action: a,
type: 'expressionRelation',
expression: (operation, context) => {
// create要保证数据的外键指向关系满足filter的约束因为这行还没有插入所以要
}
}); */
} }
return makeFilterFromRows(toBeRemoved); else {
}, /* checkers.push({
errMsg: '越权操作', entity,
}); action: a,
type: 'relation',
/* // relationFilter: (operation, context) => {
checkers.push({ const { filter } = operation;
entity: userEntityName as keyof ED, }
action: 'create' as ED[keyof ED]['Action'], }); */
type: 'data',
checker: (data, context) => {
assert(!(data instanceof Array));
const { userId } = data as ED[keyof ED]['CreateSingle']['data'];
const userId2 = context.getCurrentUserId(true);
if (userId === userId2) {
throw new OakDataException('不允许授权给自己');
} }
} }
}); }
checkers.push({
entity: userEntityName as keyof ED,
action: 'remove' as ED[keyof ED]['Action'],
type: 'row',
filter: (operation, context) => {
const userId = context.getCurrentUserId(true);
if (userId) {
return {
userId: {
$ne: userId,
},
};
}
console.warn(`没有当前用户但在删除权限,请检查。对象是${entity}`);
return {};
},
errMsg: '不允许回收自己的授权',
}); */
// 转让权限现在用update动作只允许update userId给其它人
// todo 等实现的时候再写
} }
} }

View File

@ -1,4 +1,4 @@
import { EntityDict } from "./Entity"; import { CascadeRelationItem, EntityDict } from "./Entity";
import { GenericAction } from '../actions/action'; import { GenericAction } from '../actions/action';
export type Action = string; export type Action = string;
@ -17,11 +17,7 @@ export type ActionDictOfEntityDict<E extends EntityDict> = {
}; };
}; };
export type CascadeActionItem = { // 即在cascadePath指向的对象上有relation关系。若relation为空则不限定关系
cascadePath: string;
relation: string[];
}
export type CascadeActionAuth<A extends Action = ''> = { export type CascadeActionAuth<A extends Action = ''> = {
[K in A | GenericAction]?: CascadeActionItem | (CascadeActionItem | CascadeActionItem[])[]; [K in A | GenericAction]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[];
}; };

View File

@ -1,3 +1,4 @@
import { CascadeActionAuth, RelationHierarchy, CascadeRelationAuth } from ".";
import { AsyncContext } from "../store/AsyncRowStore"; import { AsyncContext } from "../store/AsyncRowStore";
import { SyncContext } from "../store/SyncRowStore"; import { SyncContext } from "../store/SyncRowStore";
import { EntityDict, OperateOption, SelectOption } from "../types/Entity"; import { EntityDict, OperateOption, SelectOption } from "../types/Entity";
@ -56,7 +57,7 @@ export type ExpressionTask<ED extends EntityDict, T extends keyof ED> = {
filter: ED[T]['Selection']['filter']; filter: ED[T]['Selection']['filter'];
}; };
export type ExpressionTaskCombination<ED extends EntityDict> = ExpressionTask<ED, keyof ED>[][]; export type ExpressionTaskCombination<ED extends EntityDict> = ExpressionTask<ED, keyof ED>;
export type ExpressionChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = { export type ExpressionChecker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = {
priority?: number; priority?: number;
@ -89,3 +90,13 @@ export type ExpressionRelationChecker<ED extends EntityDict, T extends keyof ED,
export type Checker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> = export type Checker<ED extends EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED> | SyncContext<ED>> =
DataChecker<ED, T, Cxt> | RowChecker<ED, T, Cxt> | RelationChecker<ED, T, Cxt> | ExpressionChecker<ED, T, Cxt> | ExpressionRelationChecker<ED, T, Cxt>; DataChecker<ED, T, Cxt> | RowChecker<ED, T, Cxt> | RelationChecker<ED, T, Cxt> | ExpressionChecker<ED, T, Cxt> | ExpressionRelationChecker<ED, T, Cxt>;
export type AuthDef<ED extends EntityDict, T extends keyof ED> = {
relationAuth?: CascadeRelationAuth<NonNullable<ED[T]['Relation']>>;
actionAuth?: CascadeActionAuth<ED[T]['Action']>;
};
export type AuthDefDict<ED extends EntityDict> = {
[K in keyof ED]?: AuthDef<ED, K>;
};

View File

@ -99,6 +99,7 @@ export interface EntityDef {
CreateMulti: DeduceCreateMultipleOperation<this['Schema']>; CreateMulti: DeduceCreateMultipleOperation<this['Schema']>;
Update: DeduceUpdateOperation<this['Schema']>; Update: DeduceUpdateOperation<this['Schema']>;
Remove: DeduceRemoveOperation<this['Schema']>; Remove: DeduceRemoveOperation<this['Schema']>;
Relation?: string;
}; };
export interface EntityDict { export interface EntityDict {
@ -130,7 +131,7 @@ export type AggregationResult<SH extends GeneralEntityShape> = Array<{
}>; }>;
export type AttrFilter<SH extends GeneralEntityShape> = { export type AttrFilter<SH extends GeneralEntityShape> = {
[K in keyof SH]: any; [K in keyof SH]?: any;
} }
export type DeduceFilter<SH extends GeneralEntityShape> = MakeFilter<AttrFilter<SH> & ExprOp<keyof SH>>; export type DeduceFilter<SH extends GeneralEntityShape> = MakeFilter<AttrFilter<SH> & ExprOp<keyof SH>>;
@ -207,10 +208,10 @@ export type RelationHierarchy<R extends string> = {
export type CascadeRelationItem = { export type CascadeRelationItem = {
cascadePath: string; cascadePath: string;
relations: string[]; relations?: string[];
}; };
export type ReverseCascadeRelationHierarchy<R extends string> = { export type CascadeRelationAuth<R extends string> = {
[K in R]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[]; [K in R]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[];
}; };

View File

@ -16,5 +16,6 @@ export type Importation<ED extends EntityDict, T extends keyof ED, K extends str
id: string; id: string;
entity: T; entity: T;
headers: K[]; headers: K[];
// 解析过程中如果出错请抛出OakImportDataParseException异常
fn: (data: Partial<Record<K, string | number | boolean>>[], context: AsyncContext<ED>, option?: Record<string, any> ) => Promise<ED[T]['CreateMulti']['data']>; fn: (data: Partial<Record<K, string | number | boolean>>[], context: AsyncContext<ED>, option?: Record<string, any> ) => Promise<ED[T]['CreateMulti']['data']>;
}; };

View File

@ -1,5 +1,5 @@
import { ActionType } from '.'; import { ActionType } from '.';
import { EntityDict, EntityShape, InstinctiveAttributes, RelationHierarchy, ReverseCascadeRelationHierarchy } from './Entity'; import { EntityDict, EntityShape, InstinctiveAttributes, RelationHierarchy, CascadeRelationAuth } from './Entity';
import { DataType, DataTypeParams } from './schema/DataTypes'; import { DataType, DataTypeParams } from './schema/DataTypes';
export type Ref = 'ref'; export type Ref = 'ref';
@ -47,7 +47,7 @@ export type UniqConstraint<SH extends EntityShape> = {
type?: string; type?: string;
}; };
export interface StorageDesc<SH extends EntityShape, Relation extends string = ''> { export interface StorageDesc<SH extends EntityShape> {
storageName?: string, storageName?: string,
comment?: string, comment?: string,
attributes: Attributes<SH>; attributes: Attributes<SH>;
@ -59,13 +59,14 @@ export interface StorageDesc<SH extends EntityShape, Relation extends string = '
static?: true; // 标识是维表(变动较小,相对独立) static?: true; // 标识是维表(变动较小,相对独立)
actions: string[]; actions: string[];
actionType: ActionType; actionType: ActionType;
relationHierarchy?: RelationHierarchy<Relation>; // relationHierarchy?: RelationHierarchy<Relation>;
reverseCascadeRelationHierarchy?: ReverseCascadeRelationHierarchy<Relation>; // reverseCascadeRelationHierarchy?: ReverseCascadeRelationHierarchy<Relation>;
relation?: string[];
// view 相关 // view 相关
view?: true; view?: true;
} }
export type StorageSchema<ED extends EntityDict> = { export type StorageSchema<ED extends EntityDict> = {
[K in keyof ED]: StorageDesc<ED[K]['OpSchema'], any>; [K in keyof ED]: StorageDesc<ED[K]['OpSchema']>;
} }