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 { 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>[];

View File

@ -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;

View File

@ -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');

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 { 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>[];

View File

@ -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;

View File

@ -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[])[];
};

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

@ -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>;
};

View File

@ -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> = {

View File

@ -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']>;
};

View File

@ -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;
}

View File

@ -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)],

View File

@ -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 等实现的时候再写
}
}
}

View File

@ -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[])[];
};

View File

@ -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>;
};

View File

@ -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[])[];
};

View File

@ -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']>;
};

View File

@ -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']>;
}