checker中对actionAuth的处理,未完成
This commit is contained in:
parent
aa889f1abc
commit
bd8fd47df5
|
|
@ -1,5 +1,5 @@
|
|||
import { EntityDict } from '../base-app-domain';
|
||||
import { AsyncContext } from '../store/AsyncRowStore';
|
||||
import { SyncContext } from '../store/SyncRowStore';
|
||||
import { StorageSchema, EntityDict as BaseEntityDict, Checker } from '../types';
|
||||
export declare function createDynamicCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>): Checker<ED, keyof ED, Cxt>[];
|
||||
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>, authDict?: AuthDefDict<ED>): Checker<ED, keyof ED, Cxt>[];
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@ exports.createDynamicCheckers = void 0;
|
|||
var tslib_1 = require("tslib");
|
||||
var checker_1 = require("../store/checker");
|
||||
var modi_1 = require("../store/modi");
|
||||
function createDynamicCheckers(schema) {
|
||||
function createDynamicCheckers(schema, authDict) {
|
||||
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, 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;
|
||||
}
|
||||
exports.createDynamicCheckers = createDynamicCheckers;
|
||||
|
|
|
|||
|
|
@ -337,8 +337,8 @@ function analyzeEntity(filename, path, program, relativePath) {
|
|||
var localEnumStringTypes = [];
|
||||
var additionalImports = [];
|
||||
var localeDef = undefined;
|
||||
var relationHierarchy = undefined;
|
||||
var reverseCascadeRelationHierarchy = undefined;
|
||||
// let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
|
||||
// let reverseCascadeRelationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
|
||||
ts.forEachChild(sourceFile, function (node) {
|
||||
var _a, _b, _c, _d;
|
||||
if (ts.isImportDeclaration(node)) {
|
||||
|
|
@ -796,22 +796,22 @@ function analyzeEntity(filename, path, program, relativePath) {
|
|||
_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
|
||||
(0, assert_1.default)(hasRelationDef, "".concat(moduleName, "\u4E2D\u7684Relation\u5B9A\u4E49\u5728RelationHierarchy\u4E4B\u540E"));
|
||||
var initializer = declaration.initializer;
|
||||
(0, assert_1.default)(ts.isObjectLiteralExpression(initializer), "".concat(moduleName, "\u4E2D\u7684RelationHierarchy\u7684\u5B9A\u4E49\u5FC5\u987B\u662F\u521D\u59CB\u5316\u4E3AObjectLiteralExpress"));
|
||||
assert(hasRelationDef, `${moduleName}中的Relation定义在RelationHierarchy之后`);
|
||||
const { initializer } = declaration;
|
||||
assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`);
|
||||
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
|
||||
(0, assert_1.default)(hasRelationDef, "".concat(moduleName, "\u4E2D\u7684Relation\u5B9A\u4E49\u5728ReverseCascadeRelationHierarchy\u4E4B\u540E"));
|
||||
var initializer = declaration.initializer;
|
||||
(0, assert_1.default)(ts.isObjectLiteralExpression(initializer), "".concat(moduleName, "\u4E2D\u7684RelationHierarchy\u7684\u5B9A\u4E49\u5FC5\u987B\u662F\u521D\u59CB\u5316\u4E3AObjectLiteralExpress"));
|
||||
assert(hasRelationDef, `${moduleName}中的Relation定义在ReverseCascadeRelationHierarchy之后`);
|
||||
const { initializer } = declaration;
|
||||
assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`);
|
||||
reverseCascadeRelationHierarchy = initializer;
|
||||
}
|
||||
} */
|
||||
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,
|
||||
});
|
||||
}
|
||||
if (hasRelationDef) {
|
||||
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"));
|
||||
/* if (hasRelationDef) {
|
||||
if(!relationHierarchy && !reverseCascadeRelationHierarchy){
|
||||
console.warn(`${filename}中定义了Relation,但并没有relationHierarchy或reverseCascadeRelationHierarchy的定义,请注意自主编写权限分配的checker`);
|
||||
}
|
||||
if (relationHierarchy) {
|
||||
(0, lodash_1.assign)(schema, {
|
||||
relationHierarchy: relationHierarchy,
|
||||
assign(schema, {
|
||||
relationHierarchy,
|
||||
});
|
||||
}
|
||||
if (reverseCascadeRelationHierarchy) {
|
||||
(0, lodash_1.assign)(schema, {
|
||||
reverseCascadeRelationHierarchy: reverseCascadeRelationHierarchy,
|
||||
assign(schema, {
|
||||
reverseCascadeRelationHierarchy,
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
(0, assert_1.default)(!relationHierarchy, "".concat(filename, "\u4E2D\u5177\u6709relationHierarchy\u5B9A\u4E49\u4F46\u6CA1\u6709Relation\u5B9A\u4E49"));
|
||||
(0, assert_1.default)(!reverseCascadeRelationHierarchy, "".concat(filename, "\u4E2D\u5177\u6709reverseCascadeRelationHierarchy\u5B9A\u4E49\u4F46\u6CA1\u6709Relation\u5B9A\u4E49"));
|
||||
}
|
||||
assert(!relationHierarchy, `${filename}中具有relationHierarchy定义但没有Relation定义`);
|
||||
assert(!reverseCascadeRelationHierarchy, `${filename}中具有reverseCascadeRelationHierarchy定义但没有Relation定义`)
|
||||
} */
|
||||
(0, lodash_1.assign)(Schema, (_a = {},
|
||||
_a[moduleName] = schema,
|
||||
_a));
|
||||
|
|
@ -3066,13 +3066,19 @@ function outputStorage(outputDir, printer) {
|
|||
var entityAssignments = [];
|
||||
for (var entity in Schema) {
|
||||
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 = [
|
||||
factory.createImportSpecifier(false, undefined, factory.createIdentifier("OpSchema"))
|
||||
];
|
||||
if (relationHierarchy || reverseCascadeRelationHierarchy) {
|
||||
fromSchemaSpecifiers.push(factory.createImportSpecifier(false, undefined, factory.createIdentifier("Relation")));
|
||||
}
|
||||
/* if (relationHierarchy || reverseCascadeRelationHierarchy) {
|
||||
fromSchemaSpecifiers.push(
|
||||
factory.createImportSpecifier(
|
||||
false,
|
||||
undefined,
|
||||
factory.createIdentifier("Relation")
|
||||
)
|
||||
);
|
||||
} */
|
||||
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(fromSchemaSpecifiers)), factory.createStringLiteral("./Schema"), undefined)
|
||||
|
|
@ -3131,18 +3137,35 @@ function outputStorage(outputDir, printer) {
|
|||
if (indexExpressions.length > 0) {
|
||||
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("indexes"), factory.createArrayLiteralExpression(indexExpressions, true)));
|
||||
}
|
||||
if (relationHierarchy) {
|
||||
propertyAssignments.push(factory.createPropertyAssignment(factory.createIdentifier("relationHierarchy"), relationHierarchy));
|
||||
/* if (relationHierarchy) {
|
||||
propertyAssignments.push(
|
||||
factory.createPropertyAssignment(
|
||||
factory.createIdentifier("relationHierarchy"),
|
||||
relationHierarchy,
|
||||
)
|
||||
);
|
||||
}
|
||||
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 = [
|
||||
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)));
|
||||
var result_2 = printer.printList(ts.ListFormat.SourceFileStatements, factory.createNodeArray(statements), sourceFile);
|
||||
var filename_1 = path_1.default.join(outputDir, entity, 'Storage.ts');
|
||||
|
|
|
|||
|
|
@ -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 { AsyncContext } from "./AsyncRowStore";
|
||||
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 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>[];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
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 assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
var filter_1 = require("../store/filter");
|
||||
|
|
@ -123,6 +123,7 @@ function translateCheckerInAsyncContext(checker) {
|
|||
if (context.isRoot()) {
|
||||
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可以在此加以权限的过滤)
|
||||
_b = operation;
|
||||
_c = filter_1.combineFilters;
|
||||
|
|
@ -143,43 +144,32 @@ function translateCheckerInAsyncContext(checker) {
|
|||
return (function (_a, context, option) {
|
||||
var operation = _a.operation;
|
||||
return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||
var exprResult, result2, isLegal;
|
||||
var _this = this;
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
var exprResult, expressionEntity, expr, expressionFilter, _b, result, isLegal;
|
||||
return tslib_1.__generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0:
|
||||
if (context.isRoot() && type === 'expressionRelation') {
|
||||
return [2 /*return*/, 0];
|
||||
}
|
||||
return [4 /*yield*/, expression_1(operation, context, option)];
|
||||
case 1:
|
||||
exprResult = _b.sent();
|
||||
exprResult = _c.sent();
|
||||
if (!(typeof exprResult === 'string')) return [3 /*break*/, 2];
|
||||
throw new Exception_1.OakUserUnpermittedException(exprResult || errMsg_2);
|
||||
case 2:
|
||||
if (!(exprResult === undefined)) return [3 /*break*/, 3];
|
||||
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 () {
|
||||
var expressionEntity, expr, expressionFilter, _a, result;
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
expressionEntity = e2.entity, expr = e2.expr, expressionFilter = e2.filter;
|
||||
return [4 /*yield*/, context.select(expressionEntity, {
|
||||
data: {
|
||||
$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 3:
|
||||
expressionEntity = exprResult.entity, expr = exprResult.expr, expressionFilter = exprResult.filter;
|
||||
return [4 /*yield*/, context.select(expressionEntity, {
|
||||
data: {
|
||||
$expr: expr,
|
||||
},
|
||||
filter: expressionFilter,
|
||||
}, Object.assign({}, option, { dontCollect: true }))];
|
||||
case 4:
|
||||
result2 = _b.sent();
|
||||
isLegal = result2.find(function (r1) { return r1.every(function (r2) { return r2 === true; }); });
|
||||
_b = tslib_1.__read.apply(void 0, [_c.sent(), 1]), result = _b[0];
|
||||
isLegal = result ? result.$expr : false;
|
||||
if (!isLegal) {
|
||||
// 条件判定为假,抛异常
|
||||
if (type === 'expression') {
|
||||
|
|
@ -189,7 +179,7 @@ function translateCheckerInAsyncContext(checker) {
|
|||
throw new Exception_1.OakUserUnpermittedException(errMsg_2);
|
||||
}
|
||||
}
|
||||
_b.label = 5;
|
||||
_c.label = 5;
|
||||
case 5: return [2 /*return*/, 0];
|
||||
}
|
||||
});
|
||||
|
|
@ -260,18 +250,14 @@ function translateCheckerInSyncContext(checker) {
|
|||
}
|
||||
else {
|
||||
(0, assert_1.default)(!(exprResult instanceof Promise));
|
||||
var result2 = exprResult.map(function (e1) { return e1.map(function (e2) {
|
||||
var expressionEntity = e2.entity, expr = e2.expr, expressionFilter = e2.filter;
|
||||
var _a = tslib_1.__read(context.select(expressionEntity, {
|
||||
data: {
|
||||
$expr: expr,
|
||||
},
|
||||
filter: expressionFilter,
|
||||
}, Object.assign({}, option, { dontCollect: true })), 1), result = _a[0];
|
||||
return result ? result.$expr : false;
|
||||
}); });
|
||||
// exprResult外层是or,里层是and关系
|
||||
var isLegal = result2.find(function (r1) { return r1.every(function (r2) { return r2 === true; }); });
|
||||
var expressionEntity = exprResult.entity, expr = exprResult.expr, expressionFilter = exprResult.filter;
|
||||
var _a = tslib_1.__read(context.select(expressionEntity, {
|
||||
data: {
|
||||
$expr: expr,
|
||||
},
|
||||
filter: expressionFilter,
|
||||
}, Object.assign({}, option, { dontCollect: true })), 1), result = _a[0];
|
||||
var isLegal = result ? result.$expr : false;
|
||||
if (!isLegal) {
|
||||
// 条件判定为假,抛异常
|
||||
if (type === 'expression') {
|
||||
|
|
@ -315,247 +301,230 @@ function buildReverseHierarchyMap(relationHierarchy) {
|
|||
}
|
||||
return reverseHierarchy;
|
||||
}
|
||||
function translateSingleCascadeRelationItem(schema, lch, entity2, entityId, userId) {
|
||||
function translateCascadeRelationFilterMaker(schema, lch, entity2) {
|
||||
var cascadePath = lch.cascadePath, relations = lch.relations;
|
||||
var paths = cascadePath.split('.');
|
||||
var translateFilterIter = function (entity, iter) {
|
||||
var _a, _b, _c, _d, _e;
|
||||
var translateRelationFilter = function (entity) {
|
||||
// 有两种情况,此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]);
|
||||
if (iter === paths.length - 1) {
|
||||
if (relation === 2) {
|
||||
return {
|
||||
entity: paths[iter],
|
||||
entityId: {
|
||||
$in: {
|
||||
entity: "user".concat((0, string_1.firstLetterUpperCase)(paths[iter])),
|
||||
data: (_a = {},
|
||||
_a["".concat(paths[iter], "Id")] = 1,
|
||||
_a),
|
||||
filter: {
|
||||
userId: userId,
|
||||
relation: {
|
||||
$in: relations,
|
||||
},
|
||||
},
|
||||
},
|
||||
var filterMaker_1 = translateRelationFilter(paths[iter]);
|
||||
return function (userId) {
|
||||
var _a;
|
||||
var filter = filterMaker_1(userId);
|
||||
if (filter.$in) {
|
||||
return {
|
||||
entity: paths[iter],
|
||||
entityId: filter
|
||||
};
|
||||
}
|
||||
return _a = {},
|
||||
_a[paths[iter]] = filter,
|
||||
_a;
|
||||
};
|
||||
}
|
||||
(0, assert_1.default)(typeof relation === 'string');
|
||||
return _b = {},
|
||||
_b["".concat(paths[iter], "Id")] = {
|
||||
$in: {
|
||||
entity: "user".concat((0, string_1.firstLetterUpperCase)(relation)),
|
||||
data: (_c = {},
|
||||
_c["".concat(relation, "Id")] = 1,
|
||||
_c),
|
||||
filter: {
|
||||
userId: userId,
|
||||
relation: {
|
||||
$in: relations,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
_b;
|
||||
var filterMaker_2 = translateRelationFilter(relation);
|
||||
return function (userId) {
|
||||
var _a, _b;
|
||||
var filter = filterMaker_2(userId);
|
||||
if (filter.$in) {
|
||||
return _a = {},
|
||||
_a["".concat(paths[iter], "Id")] = filter,
|
||||
_a;
|
||||
}
|
||||
return _b = {},
|
||||
_b[paths[iter]] = filter,
|
||||
_b;
|
||||
};
|
||||
}
|
||||
else {
|
||||
var subFilter = translateFilterIter(paths[iter], iter + 1);
|
||||
var subFilterMaker_1 = translateFilterMakerIter(paths[iter], iter + 1);
|
||||
if (iter === 0) {
|
||||
return _d = {},
|
||||
_d[paths[iter]] = subFilter,
|
||||
_d.id = entityId,
|
||||
_d;
|
||||
return function (userId) {
|
||||
var _a;
|
||||
var subFilter = subFilterMaker_1(userId);
|
||||
return _a = {},
|
||||
_a[paths[iter]] = subFilter,
|
||||
_a;
|
||||
};
|
||||
}
|
||||
return _e = {},
|
||||
_e[paths[iter]] = subFilter,
|
||||
_e;
|
||||
return function (userId) {
|
||||
var _a;
|
||||
return (_a = {},
|
||||
_a[paths[iter]] = subFilterMaker_1(userId),
|
||||
_a);
|
||||
};
|
||||
}
|
||||
};
|
||||
var filter = translateFilterIter(entity2, 0);
|
||||
return {
|
||||
entity: entity2,
|
||||
filter: filter,
|
||||
expr: {
|
||||
$gt: [{
|
||||
'#attr': '$$createAt$$',
|
||||
}, 0]
|
||||
},
|
||||
};
|
||||
var filter = paths.length > 0 ? translateFilterMakerIter(entity2, 0) : translateRelationFilter(entity2);
|
||||
return filter;
|
||||
}
|
||||
function translateFromCascadeRelationHierarchy(schema, legalCascadeHierarchies, entity, entityId, userId) {
|
||||
if (legalCascadeHierarchies instanceof Array) {
|
||||
return legalCascadeHierarchies.map(function (ele) {
|
||||
function translateActionAuthFilterMaker(schema, relationItem, entity) {
|
||||
if (relationItem instanceof Array) {
|
||||
var maker_1 = relationItem.map(function (ele) {
|
||||
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 {
|
||||
return [[translateSingleCascadeRelationItem(schema, legalCascadeHierarchies, entity, entityId, userId)]];
|
||||
}
|
||||
var filterMaker = translateCascadeRelationFilterMaker(schema, relationItem, entity);
|
||||
return function (userId) { return filterMaker(userId); };
|
||||
}
|
||||
function makeRelationExpressionCombination(schema, entity, entityId, userId, relation, reverseHierarchy, reverseCascadeRelationHierarchy) {
|
||||
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) {
|
||||
function createAuthCheckers(schema, authDict) {
|
||||
var checkers = [];
|
||||
var _loop_1 = function (entity) {
|
||||
var _a = schema[entity], relationHierarchy = _a.relationHierarchy, reverseCascadeRelationHierarchy = _a.reverseCascadeRelationHierarchy;
|
||||
if (relationHierarchy || reverseCascadeRelationHierarchy) {
|
||||
var reverseHierarchy_1 = relationHierarchy && buildReverseHierarchyMap(relationHierarchy);
|
||||
var userEntityName_1 = "user".concat((0, string_1.firstLetterUpperCase)(entity));
|
||||
var entityIdAttr_1 = "".concat(entity, "Id");
|
||||
checkers.push({
|
||||
entity: userEntityName_1,
|
||||
action: 'create',
|
||||
type: 'expressionRelation',
|
||||
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 _a, _b;
|
||||
if (authDict[entity]) {
|
||||
var _c = authDict[entity], relationAuth = _c.relationAuth, actionAuth = _c.actionAuth;
|
||||
if (relationAuth) {
|
||||
var raFilterMakerDict_1 = {};
|
||||
for (var r in relationAuth) {
|
||||
Object.assign(raFilterMakerDict_1, (_a = {},
|
||||
_a[r] = translateActionAuthFilterMaker(schema, relationAuth[r], entity),
|
||||
_a));
|
||||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
var userEntityName_1 = "user".concat((0, string_1.firstLetterUpperCase)(entity));
|
||||
var entityIdAttr_1 = "".concat(entity, "Id");
|
||||
checkers.push({
|
||||
entity: userEntityName_1,
|
||||
action: 'create',
|
||||
type: 'expressionRelation',
|
||||
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();
|
||||
if (!raFilterMakerDict_1[relation]) {
|
||||
return;
|
||||
}
|
||||
var filter = raFilterMakerDict_1[relation](userId);
|
||||
return {
|
||||
userId: {
|
||||
$ne: userId,
|
||||
entity: entity,
|
||||
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}`);
|
||||
return {};
|
||||
},
|
||||
errMsg: '不允许回收自己的授权',
|
||||
}); */
|
||||
// 转让权限现在用update动作,只允许update userId给其它人
|
||||
// todo 等实现的时候再写
|
||||
else {
|
||||
/* checkers.push({
|
||||
entity,
|
||||
action: a,
|
||||
type: 'relation',
|
||||
relationFilter: (operation, context) => {
|
||||
const { filter } = operation;
|
||||
}
|
||||
}); */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
for (var entity in schema) {
|
||||
|
|
@ -563,4 +532,4 @@ function createRelationHierarchyCheckers(schema) {
|
|||
}
|
||||
return checkers;
|
||||
}
|
||||
exports.createRelationHierarchyCheckers = createRelationHierarchyCheckers;
|
||||
exports.createAuthCheckers = createAuthCheckers;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { EntityDict } from "./Entity";
|
||||
import { CascadeRelationItem, EntityDict } from "./Entity";
|
||||
import { GenericAction } from '../actions/action';
|
||||
export declare type Action = 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>;
|
||||
};
|
||||
};
|
||||
export declare type CascadeActionItem = {
|
||||
cascadePath: string;
|
||||
relation: string[];
|
||||
};
|
||||
export declare type CascadeActionAuth<A extends Action = ''> = {
|
||||
[K in A | GenericAction]?: CascadeActionItem | (CascadeActionItem | CascadeActionItem[])[];
|
||||
[K in A | GenericAction]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { CascadeActionAuth, CascadeRelationAuth } from ".";
|
||||
import { AsyncContext } from "../store/AsyncRowStore";
|
||||
import { SyncContext } from "../store/SyncRowStore";
|
||||
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']>;
|
||||
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>> = {
|
||||
priority?: number;
|
||||
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']);
|
||||
};
|
||||
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>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ export interface EntityDef {
|
|||
CreateMulti: DeduceCreateMultipleOperation<this['Schema']>;
|
||||
Update: DeduceUpdateOperation<this['Schema']>;
|
||||
Remove: DeduceRemoveOperation<this['Schema']>;
|
||||
Relation?: string;
|
||||
}
|
||||
export interface EntityDict {
|
||||
[E: string]: EntityDef;
|
||||
|
|
@ -104,7 +105,7 @@ export declare type AggregationResult<SH extends GeneralEntityShape> = Array<{
|
|||
'#data'?: Partial<SH>;
|
||||
}>;
|
||||
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 DeduceSorterAttr<SH extends GeneralEntityShape> = OneOf<{
|
||||
|
|
@ -155,9 +156,9 @@ export declare type RelationHierarchy<R extends string> = {
|
|||
};
|
||||
export declare type CascadeRelationItem = {
|
||||
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[])[];
|
||||
};
|
||||
export declare type SelectOpResult<ED extends EntityDict> = {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { ActionType } from '.';
|
||||
import { EntityDict, EntityShape, InstinctiveAttributes, RelationHierarchy, ReverseCascadeRelationHierarchy } from './Entity';
|
||||
import { EntityDict, EntityShape, InstinctiveAttributes } from './Entity';
|
||||
import { DataType, DataTypeParams } from './schema/DataTypes';
|
||||
export declare type Ref = 'ref';
|
||||
export interface Column<SH extends EntityShape> {
|
||||
|
|
@ -36,7 +36,7 @@ export declare type UniqConstraint<SH extends EntityShape> = {
|
|||
attributes: Array<keyof SH>;
|
||||
type?: string;
|
||||
};
|
||||
export interface StorageDesc<SH extends EntityShape, Relation extends string = ''> {
|
||||
export interface StorageDesc<SH extends EntityShape> {
|
||||
storageName?: string;
|
||||
comment?: string;
|
||||
attributes: Attributes<SH>;
|
||||
|
|
@ -48,10 +48,9 @@ export interface StorageDesc<SH extends EntityShape, Relation extends string = '
|
|||
static?: true;
|
||||
actions: string[];
|
||||
actionType: ActionType;
|
||||
relationHierarchy?: RelationHierarchy<Relation>;
|
||||
reverseCascadeRelationHierarchy?: ReverseCascadeRelationHierarchy<Relation>;
|
||||
relation?: string[];
|
||||
view?: true;
|
||||
}
|
||||
export declare type StorageSchema<ED extends EntityDict> = {
|
||||
[K in keyof ED]: StorageDesc<ED[K]['OpSchema'], any>;
|
||||
[K in keyof ED]: StorageDesc<ED[K]['OpSchema']>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
import { EntityDict } from '../base-app-domain';
|
||||
import { AsyncContext } from '../store/AsyncRowStore';
|
||||
import { createRelationHierarchyCheckers } from '../store/checker';
|
||||
import { createAuthCheckers } from '../store/checker';
|
||||
import { createModiRelatedCheckers } from '../store/modi';
|
||||
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>[] = [];
|
||||
checkers.push(...createModiRelatedCheckers<ED, Cxt>(schema));
|
||||
checkers.push(...createRelationHierarchyCheckers<ED, Cxt>(schema));
|
||||
if (authDict) {
|
||||
checkers.push(...createAuthCheckers<ED, Cxt>(schema, authDict));
|
||||
}
|
||||
return checkers;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ const Schema: Record<string, {
|
|||
states: string[];
|
||||
sourceFile: ts.SourceFile;
|
||||
locale: ts.ObjectLiteralExpression;
|
||||
relationHierarchy?: ts.ObjectLiteralExpression;
|
||||
reverseCascadeRelationHierarchy?: ts.ObjectLiteralExpression;
|
||||
// relationHierarchy?: ts.ObjectLiteralExpression;
|
||||
// reverseCascadeRelationHierarchy?: ts.ObjectLiteralExpression;
|
||||
toModi: boolean;
|
||||
actionType: string;
|
||||
static: boolean;
|
||||
|
|
@ -498,8 +498,8 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
|||
const localEnumStringTypes: string[] = [];
|
||||
const additionalImports: ts.ImportDeclaration[] = [];
|
||||
let localeDef: ts.ObjectLiteralExpression | undefined = undefined;
|
||||
let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
|
||||
let reverseCascadeRelationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
|
||||
// let relationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
|
||||
// let reverseCascadeRelationHierarchy: ts.ObjectLiteralExpression | undefined = undefined;
|
||||
ts.forEachChild(sourceFile!, (node) => {
|
||||
if (ts.isImportDeclaration(node)) {
|
||||
const entityImported = getEntityImported(node);
|
||||
|
|
@ -1125,7 +1125,7 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
|||
_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
|
||||
assert(hasRelationDef, `${moduleName}中的Relation定义在RelationHierarchy之后`);
|
||||
const { initializer } = declaration;
|
||||
|
|
@ -1138,9 +1138,9 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
|||
const { initializer } = declaration;
|
||||
assert(ts.isObjectLiteralExpression(initializer!), `${moduleName}中的RelationHierarchy的定义必须是初始化为ObjectLiteralExpress`);
|
||||
reverseCascadeRelationHierarchy = initializer;
|
||||
}
|
||||
} */
|
||||
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,
|
||||
});
|
||||
}
|
||||
if (hasRelationDef) {
|
||||
/* if (hasRelationDef) {
|
||||
if(!relationHierarchy && !reverseCascadeRelationHierarchy){
|
||||
console.warn(`${filename}中定义了Relation,但并没有relationHierarchy或reverseCascadeRelationHierarchy的定义,请注意自主编写权限分配的checker`);
|
||||
}
|
||||
|
|
@ -1200,7 +1200,7 @@ function analyzeEntity(filename: string, path: string, program: ts.Program, rela
|
|||
else {
|
||||
assert(!relationHierarchy, `${filename}中具有relationHierarchy定义但没有Relation定义`);
|
||||
assert(!reverseCascadeRelationHierarchy, `${filename}中具有reverseCascadeRelationHierarchy定义但没有Relation定义`)
|
||||
}
|
||||
} */
|
||||
|
||||
assign(Schema, {
|
||||
[moduleName]: schema,
|
||||
|
|
@ -5664,7 +5664,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
|
|||
|
||||
for (const entity in Schema) {
|
||||
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 = [
|
||||
factory.createImportSpecifier(
|
||||
false,
|
||||
|
|
@ -5672,7 +5672,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
|
|||
factory.createIdentifier("OpSchema")
|
||||
)
|
||||
];
|
||||
if (relationHierarchy || reverseCascadeRelationHierarchy) {
|
||||
/* if (relationHierarchy || reverseCascadeRelationHierarchy) {
|
||||
fromSchemaSpecifiers.push(
|
||||
factory.createImportSpecifier(
|
||||
false,
|
||||
|
|
@ -5680,7 +5680,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
|
|||
factory.createIdentifier("Relation")
|
||||
)
|
||||
);
|
||||
}
|
||||
} */
|
||||
const statements: ts.Statement[] = [
|
||||
factory.createImportDeclaration(
|
||||
undefined,
|
||||
|
|
@ -5881,7 +5881,7 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
|
|||
)
|
||||
);
|
||||
}
|
||||
if (relationHierarchy) {
|
||||
/* if (relationHierarchy) {
|
||||
propertyAssignments.push(
|
||||
factory.createPropertyAssignment(
|
||||
factory.createIdentifier("relationHierarchy"),
|
||||
|
|
@ -5896,21 +5896,32 @@ function outputStorage(outputDir: string, printer: ts.Printer) {
|
|||
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 = [
|
||||
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)],
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import assert from 'assert';
|
||||
import { addFilterSegment, checkFilterContains, combineFilters } from "../store/filter";
|
||||
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 { AsyncContext } from "./AsyncRowStore";
|
||||
import { getFullProjection } from './actionDef';
|
||||
|
|
@ -90,6 +90,7 @@ export function translateCheckerInAsyncContext<
|
|||
if (context.isRoot()) {
|
||||
return 0;
|
||||
}
|
||||
assert(operation.action !== 'create', `${entity as string}上的create动作定义了relation类型的checker,请使用expressionRelation替代`);
|
||||
// 对后台而言,将生成的relationFilter加到filter之上(select可以在此加以权限的过滤)
|
||||
operation.filter = combineFilters([operation.filter, await relationFilter(operation, context, option)]);
|
||||
return 0;
|
||||
|
|
@ -110,30 +111,14 @@ export function translateCheckerInAsyncContext<
|
|||
return 0;
|
||||
}
|
||||
else {
|
||||
const result2 = await Promise.all(
|
||||
exprResult.map(
|
||||
(e1) => Promise.all(
|
||||
e1.map(
|
||||
async (e2) => {
|
||||
const { entity: expressionEntity, expr, filter: expressionFilter } = e2;
|
||||
const [result] = await context.select(expressionEntity, {
|
||||
data: {
|
||||
$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
|
||||
)
|
||||
);
|
||||
const { entity: expressionEntity, expr, filter: expressionFilter } = exprResult;
|
||||
const [result] = await context.select(expressionEntity, {
|
||||
data: {
|
||||
$expr: expr,
|
||||
},
|
||||
filter: expressionFilter,
|
||||
}, Object.assign({}, option, { dontCollect: true }));
|
||||
const isLegal = result ? result.$expr as boolean : false;
|
||||
if (!isLegal) {
|
||||
// 条件判定为假,抛异常
|
||||
if (type === 'expression') {
|
||||
|
|
@ -215,26 +200,14 @@ export function translateCheckerInSyncContext<
|
|||
}
|
||||
else {
|
||||
assert(!(exprResult instanceof Promise));
|
||||
const result2 = exprResult.map(
|
||||
(e1) => e1.map(
|
||||
(e2) => {
|
||||
const { entity: expressionEntity, expr, filter: expressionFilter } = e2;
|
||||
const [result] = context.select(expressionEntity, {
|
||||
data: {
|
||||
$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
|
||||
)
|
||||
);
|
||||
const { entity: expressionEntity, expr, filter: expressionFilter } = exprResult;
|
||||
const [result] = context.select(expressionEntity, {
|
||||
data: {
|
||||
$expr: expr,
|
||||
},
|
||||
filter: expressionFilter,
|
||||
}, Object.assign({}, option, { dontCollect: true }));
|
||||
const isLegal = result ? result.$expr as boolean : false;
|
||||
if (!isLegal) {
|
||||
// 条件判定为假,抛异常
|
||||
if (type === 'expression') {
|
||||
|
|
@ -269,273 +242,255 @@ function buildReverseHierarchyMap(relationHierarchy: RelationHierarchy<any>) {
|
|||
return reverseHierarchy;
|
||||
}
|
||||
|
||||
function translateSingleCascadeRelationItem<ED extends EntityDict & BaseEntityDict>(
|
||||
function translateCascadeRelationFilterMaker<ED extends EntityDict & BaseEntityDict>(
|
||||
schema: StorageSchema<ED>,
|
||||
lch: CascadeRelationItem,
|
||||
entity2: keyof ED,
|
||||
entityId: string,
|
||||
userId: string): ExpressionTask<ED, keyof ED> {
|
||||
entity2: keyof ED): (userId: string) => ExpressionTask<ED, keyof ED>['filter'] {
|
||||
const { cascadePath, relations } = lch;
|
||||
const paths = cascadePath.split('.');
|
||||
|
||||
const translateFilterIter = <T extends keyof ED>(entity: keyof ED, iter: number): ED[T]['Selection']['filter'] => {
|
||||
const relation = judgeRelation(schema, entity, paths[iter]);
|
||||
if (iter === paths.length - 1) {
|
||||
if (relation === 2) {
|
||||
return {
|
||||
entity: paths[iter],
|
||||
entityId: {
|
||||
$in: {
|
||||
entity: `user${firstLetterUpperCase(paths[iter])}`,
|
||||
data: {
|
||||
[`${paths[iter]}Id`]: 1,
|
||||
},
|
||||
filter: {
|
||||
userId,
|
||||
relation: {
|
||||
$in: relations,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
const translateRelationFilter = <T extends keyof ED>(entity: T): (userId: string) => ED[T]['Selection']['filter'] => {
|
||||
// 有两种情况,此entity和user有Relation定义,或是此entity上有userId
|
||||
if (schema[entity].relation) {
|
||||
const relationEntityName = `user${firstLetterUpperCase(entity as string)}`;
|
||||
return (userId) => {
|
||||
const filter = relations ? {
|
||||
userId,
|
||||
relation: {
|
||||
$in: relations,
|
||||
},
|
||||
} : {
|
||||
userId,
|
||||
};
|
||||
}
|
||||
assert(typeof relation === 'string');
|
||||
return {
|
||||
[`${paths[iter]}Id`]: {
|
||||
$in: {
|
||||
entity: `user${firstLetterUpperCase(relation)}`,
|
||||
data: {
|
||||
[`${relation}Id`]: 1,
|
||||
},
|
||||
filter: {
|
||||
userId,
|
||||
relation: {
|
||||
$in: relations,
|
||||
return {
|
||||
id: {
|
||||
$in: {
|
||||
entity: relationEntityName,
|
||||
data: {
|
||||
[`${entity as string}Id`]: 1,
|
||||
},
|
||||
filter,
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
const subFilter = translateFilterIter(paths[iter], iter + 1);
|
||||
if (iter === 0) {
|
||||
|
||||
const { attributes } = schema[entity];
|
||||
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 {
|
||||
[paths[iter]]: subFilter,
|
||||
id: entityId,
|
||||
[paths[iter]]: filter,
|
||||
};
|
||||
}
|
||||
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 {
|
||||
entity: entity2,
|
||||
filter,
|
||||
expr: {
|
||||
$gt: [{
|
||||
'#attr': '$$createAt$$',
|
||||
}, 0]
|
||||
},
|
||||
};
|
||||
|
||||
const filter = paths.length > 0 ? translateFilterMakerIter(entity2, 0) : translateRelationFilter(entity2);
|
||||
return filter;
|
||||
}
|
||||
|
||||
function translateFromCascadeRelationHierarchy<ED extends EntityDict & BaseEntityDict>(
|
||||
function translateActionAuthFilterMaker<ED extends EntityDict & BaseEntityDict>(
|
||||
schema: StorageSchema<ED>,
|
||||
legalCascadeHierarchies: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[],
|
||||
entity: keyof ED,
|
||||
entityId: string,
|
||||
userId: string): ExpressionTaskCombination<ED> {
|
||||
if (legalCascadeHierarchies instanceof Array) {
|
||||
return legalCascadeHierarchies.map(
|
||||
relationItem: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[],
|
||||
entity: keyof ED
|
||||
): (userId: string) => ED[keyof ED]['Selection']['filter'] {
|
||||
if (relationItem instanceof Array) {
|
||||
const maker = relationItem.map(
|
||||
ele => {
|
||||
if (ele instanceof Array) {
|
||||
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)];
|
||||
}
|
||||
)
|
||||
}
|
||||
else {
|
||||
return [[translateSingleCascadeRelationItem(schema, legalCascadeHierarchies, entity, entityId, userId)]];
|
||||
);
|
||||
return (userId) => ({
|
||||
$or: maker.map(
|
||||
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>,
|
||||
entity: keyof 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>) {
|
||||
authDict: AuthDefDict<ED>) {
|
||||
const checkers: Checker<ED, keyof ED, Cxt>[] = [];
|
||||
|
||||
for (const entity in schema) {
|
||||
const { relationHierarchy, reverseCascadeRelationHierarchy } = schema[entity];
|
||||
if (relationHierarchy || reverseCascadeRelationHierarchy) {
|
||||
const reverseHierarchy = relationHierarchy && buildReverseHierarchyMap(relationHierarchy);
|
||||
const userEntityName = `user${firstLetterUpperCase(entity)}`;
|
||||
const entityIdAttr = `${entity}Id`;
|
||||
checkers.push({
|
||||
entity: userEntityName as keyof ED,
|
||||
action: 'create',
|
||||
type: 'expressionRelation',
|
||||
expression: <T2 extends keyof ED>(operation: any, context: Cxt) => {
|
||||
const { data } = operation as ED[keyof ED]['Create'];
|
||||
assert(!(data instanceof Array));
|
||||
const { relation, [entityIdAttr]: entityId } = data;
|
||||
const userId = context.getCurrentUserId();
|
||||
const schema = context.getSchema();
|
||||
return makeRelationExpressionCombination(schema, entity, entityId, userId!, relation, reverseHierarchy, reverseCascadeRelationHierarchy);
|
||||
},
|
||||
errMsg: '越权操作',
|
||||
});
|
||||
checkers.push({
|
||||
entity: userEntityName as keyof ED,
|
||||
action: 'remove',
|
||||
type: 'expressionRelation',
|
||||
expression: <T2 extends keyof ED>(operation: any, context: Cxt) => {
|
||||
const userId = context.getCurrentUserId();
|
||||
const { filter } = operation as ED[keyof ED]['Remove'];
|
||||
const makeFilterFromRows = (rows: Partial<ED[T2]['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 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;
|
||||
if (authDict[entity]) {
|
||||
const { relationAuth, actionAuth } = authDict[entity]!;
|
||||
if (relationAuth) {
|
||||
const raFilterMakerDict = {} as Record<string, (userId: string) => ED[keyof ED]['Selection']['filter']>;
|
||||
for (const r in relationAuth) {
|
||||
Object.assign(raFilterMakerDict, {
|
||||
[r]: translateActionAuthFilterMaker(schema, relationAuth[r as NonNullable<ED[keyof ED]['Relation']>]!, entity),
|
||||
});
|
||||
}
|
||||
const userEntityName = `user${firstLetterUpperCase(entity)}`;
|
||||
const entityIdAttr = `${entity}Id`;
|
||||
checkers.push({
|
||||
entity: userEntityName as keyof ED,
|
||||
action: 'create',
|
||||
type: 'expressionRelation',
|
||||
expression: (operation, context) => {
|
||||
const { data } = operation as ED[keyof ED]['Create'];
|
||||
assert(!(data instanceof Array));
|
||||
const { relation, [entityIdAttr]: entityId } = data;
|
||||
const userId = context.getCurrentUserId();
|
||||
if (!raFilterMakerDict[relation]) {
|
||||
return;
|
||||
}
|
||||
const filter = raFilterMakerDict[relation]!(userId!);
|
||||
return {
|
||||
entity,
|
||||
filter,
|
||||
expr: {
|
||||
$gt: [{
|
||||
'#attr': '$$createAt$$',
|
||||
}, 0]
|
||||
},
|
||||
[[]] 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, {
|
||||
data: {
|
||||
id: 1,
|
||||
relation: 1,
|
||||
[entityIdAttr]: 1,
|
||||
},
|
||||
filter,
|
||||
}, { dontCollect: true });
|
||||
if (toBeRemoved instanceof Promise) {
|
||||
return toBeRemoved.then(
|
||||
(rows) => makeFilterFromRows(rows)
|
||||
);
|
||||
// 所有的relation条件要同时满足and关系(注意这里的filter翻译出来是在entity对象上,不是在userEntity对象上)
|
||||
return {
|
||||
$and: relations.map(
|
||||
(relation) => raFilterMakerDict[relation!]
|
||||
).filter(
|
||||
ele => !!ele
|
||||
).map(
|
||||
ele => ({
|
||||
[entity]: ele(userId!),
|
||||
})
|
||||
)
|
||||
} 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);
|
||||
},
|
||||
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('不允许授权给自己');
|
||||
else {
|
||||
/* checkers.push({
|
||||
entity,
|
||||
action: a,
|
||||
type: 'relation',
|
||||
relationFilter: (operation, context) => {
|
||||
const { filter } = operation;
|
||||
}
|
||||
}); */
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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 等实现的时候再写
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { EntityDict } from "./Entity";
|
||||
import { CascadeRelationItem, EntityDict } from "./Entity";
|
||||
import { GenericAction } from '../actions/action';
|
||||
|
||||
export type Action = string;
|
||||
|
|
@ -17,11 +17,7 @@ export type ActionDictOfEntityDict<E extends EntityDict> = {
|
|||
};
|
||||
};
|
||||
|
||||
export type CascadeActionItem = {
|
||||
cascadePath: string;
|
||||
relation: string[];
|
||||
}
|
||||
|
||||
// 即在cascadePath指向的对象上,有relation关系。若relation为空则不限定关系
|
||||
export type CascadeActionAuth<A extends Action = ''> = {
|
||||
[K in A | GenericAction]?: CascadeActionItem | (CascadeActionItem | CascadeActionItem[])[];
|
||||
[K in A | GenericAction]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { CascadeActionAuth, RelationHierarchy, CascadeRelationAuth } from ".";
|
||||
import { AsyncContext } from "../store/AsyncRowStore";
|
||||
import { SyncContext } from "../store/SyncRowStore";
|
||||
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'];
|
||||
};
|
||||
|
||||
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>> = {
|
||||
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>> =
|
||||
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>;
|
||||
};
|
||||
|
|
@ -99,6 +99,7 @@ export interface EntityDef {
|
|||
CreateMulti: DeduceCreateMultipleOperation<this['Schema']>;
|
||||
Update: DeduceUpdateOperation<this['Schema']>;
|
||||
Remove: DeduceRemoveOperation<this['Schema']>;
|
||||
Relation?: string;
|
||||
};
|
||||
|
||||
export interface EntityDict {
|
||||
|
|
@ -130,7 +131,7 @@ export type AggregationResult<SH extends GeneralEntityShape> = Array<{
|
|||
}>;
|
||||
|
||||
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>>;
|
||||
|
|
@ -207,10 +208,10 @@ export type RelationHierarchy<R extends string> = {
|
|||
|
||||
export type CascadeRelationItem = {
|
||||
cascadePath: string;
|
||||
relations: string[];
|
||||
relations?: string[];
|
||||
};
|
||||
|
||||
export type ReverseCascadeRelationHierarchy<R extends string> = {
|
||||
export type CascadeRelationAuth<R extends string> = {
|
||||
[K in R]?: CascadeRelationItem | (CascadeRelationItem | CascadeRelationItem[])[];
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -16,5 +16,6 @@ export type Importation<ED extends EntityDict, T extends keyof ED, K extends str
|
|||
id: string;
|
||||
entity: T;
|
||||
headers: K[];
|
||||
// 解析过程中如果出错,请抛出OakImportDataParseException异常
|
||||
fn: (data: Partial<Record<K, string | number | boolean>>[], context: AsyncContext<ED>, option?: Record<string, any> ) => Promise<ED[T]['CreateMulti']['data']>;
|
||||
};
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
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';
|
||||
|
||||
export type Ref = 'ref';
|
||||
|
|
@ -47,7 +47,7 @@ export type UniqConstraint<SH extends EntityShape> = {
|
|||
type?: string;
|
||||
};
|
||||
|
||||
export interface StorageDesc<SH extends EntityShape, Relation extends string = ''> {
|
||||
export interface StorageDesc<SH extends EntityShape> {
|
||||
storageName?: string,
|
||||
comment?: string,
|
||||
attributes: Attributes<SH>;
|
||||
|
|
@ -59,13 +59,14 @@ export interface StorageDesc<SH extends EntityShape, Relation extends string = '
|
|||
static?: true; // 标识是维表(变动较小,相对独立)
|
||||
actions: string[];
|
||||
actionType: ActionType;
|
||||
relationHierarchy?: RelationHierarchy<Relation>;
|
||||
reverseCascadeRelationHierarchy?: ReverseCascadeRelationHierarchy<Relation>;
|
||||
// relationHierarchy?: RelationHierarchy<Relation>;
|
||||
// reverseCascadeRelationHierarchy?: ReverseCascadeRelationHierarchy<Relation>;
|
||||
relation?: string[];
|
||||
// view 相关
|
||||
view?: true;
|
||||
}
|
||||
|
||||
|
||||
export type StorageSchema<ED extends EntityDict> = {
|
||||
[K in keyof ED]: StorageDesc<ED[K]['OpSchema'], any>;
|
||||
[K in keyof ED]: StorageDesc<ED[K]['OpSchema']>;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue