zenstack 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +194 -1
- package/bin/cli +3 -0
- package/bin/post-install.js +24 -0
- package/cli/actions/generate.d.ts +13 -0
- package/cli/actions/generate.js +71 -0
- package/cli/actions/generate.js.map +1 -0
- package/cli/actions/index.d.ts +3 -0
- package/cli/actions/index.js +20 -0
- package/cli/actions/index.js.map +1 -0
- package/cli/actions/info.d.ts +4 -0
- package/cli/actions/info.js +63 -0
- package/cli/actions/info.js.map +1 -0
- package/cli/actions/init.d.ts +12 -0
- package/cli/actions/init.js +83 -0
- package/cli/actions/init.js.map +1 -0
- package/cli/cli-error.d.ts +5 -0
- package/cli/cli-error.js +10 -0
- package/cli/cli-error.js.map +1 -0
- package/cli/cli-util.d.ts +21 -0
- package/cli/cli-util.js +211 -0
- package/cli/cli-util.js.map +1 -0
- package/cli/config.d.ts +10 -0
- package/cli/config.js +62 -0
- package/cli/config.js.map +1 -0
- package/cli/index.d.ts +7 -0
- package/cli/index.js +128 -0
- package/cli/index.js.map +1 -0
- package/cli/plugin-runner.d.ts +24 -0
- package/cli/plugin-runner.js +229 -0
- package/cli/plugin-runner.js.map +1 -0
- package/constants.d.ts +1 -0
- package/constants.js +6 -0
- package/constants.js.map +1 -0
- package/language-server/constants.d.ts +22 -0
- package/language-server/constants.js +27 -0
- package/language-server/constants.js.map +1 -0
- package/language-server/main.d.ts +1 -0
- package/language-server/main.js +13 -0
- package/language-server/main.js.map +1 -0
- package/language-server/types.d.ts +10 -0
- package/language-server/types.js +3 -0
- package/language-server/types.js.map +1 -0
- package/language-server/utils.d.ts +5 -0
- package/language-server/utils.js +22 -0
- package/language-server/utils.js.map +1 -0
- package/language-server/validator/attribute-application-validator.d.ts +15 -0
- package/language-server/validator/attribute-application-validator.js +246 -0
- package/language-server/validator/attribute-application-validator.js.map +1 -0
- package/language-server/validator/attribute-validator.d.ts +9 -0
- package/language-server/validator/attribute-validator.js +14 -0
- package/language-server/validator/attribute-validator.js.map +1 -0
- package/language-server/validator/datamodel-validator.d.ts +22 -0
- package/language-server/validator/datamodel-validator.js +329 -0
- package/language-server/validator/datamodel-validator.js.map +1 -0
- package/language-server/validator/datasource-validator.d.ts +12 -0
- package/language-server/validator/datasource-validator.js +66 -0
- package/language-server/validator/datasource-validator.js.map +1 -0
- package/language-server/validator/enum-validator.d.ts +11 -0
- package/language-server/validator/enum-validator.js +25 -0
- package/language-server/validator/enum-validator.js.map +1 -0
- package/language-server/validator/expression-validator.d.ts +10 -0
- package/language-server/validator/expression-validator.js +135 -0
- package/language-server/validator/expression-validator.js.map +1 -0
- package/language-server/validator/function-decl-validator.d.ts +9 -0
- package/language-server/validator/function-decl-validator.js +13 -0
- package/language-server/validator/function-decl-validator.js.map +1 -0
- package/language-server/validator/function-invocation-validator.d.ts +11 -0
- package/language-server/validator/function-invocation-validator.js +135 -0
- package/language-server/validator/function-invocation-validator.js.map +1 -0
- package/language-server/validator/schema-validator.d.ts +13 -0
- package/language-server/validator/schema-validator.js +49 -0
- package/language-server/validator/schema-validator.js.map +1 -0
- package/language-server/validator/utils.d.ts +24 -0
- package/language-server/validator/utils.js +154 -0
- package/language-server/validator/utils.js.map +1 -0
- package/language-server/validator/zmodel-validator.d.ts +25 -0
- package/language-server/validator/zmodel-validator.js +83 -0
- package/language-server/validator/zmodel-validator.js.map +1 -0
- package/language-server/zmodel-code-action.d.ts +15 -0
- package/language-server/zmodel-code-action.js +118 -0
- package/language-server/zmodel-code-action.js.map +1 -0
- package/language-server/zmodel-definition.d.ts +7 -0
- package/language-server/zmodel-definition.js +31 -0
- package/language-server/zmodel-definition.js.map +1 -0
- package/language-server/zmodel-formatter.d.ts +9 -0
- package/language-server/zmodel-formatter.js +76 -0
- package/language-server/zmodel-formatter.js.map +1 -0
- package/language-server/zmodel-linker.d.ts +32 -0
- package/language-server/zmodel-linker.js +447 -0
- package/language-server/zmodel-linker.js.map +1 -0
- package/language-server/zmodel-module.d.ts +41 -0
- package/language-server/zmodel-module.js +83 -0
- package/language-server/zmodel-module.js.map +1 -0
- package/language-server/zmodel-scope.d.ts +16 -0
- package/language-server/zmodel-scope.js +100 -0
- package/language-server/zmodel-scope.js.map +1 -0
- package/language-server/zmodel-workspace-manager.d.ts +12 -0
- package/language-server/zmodel-workspace-manager.js +138 -0
- package/language-server/zmodel-workspace-manager.js.map +1 -0
- package/package.json +140 -8
- package/plugins/access-policy/expression-writer.d.ts +46 -0
- package/plugins/access-policy/expression-writer.js +580 -0
- package/plugins/access-policy/expression-writer.js.map +1 -0
- package/plugins/access-policy/index.d.ts +4 -0
- package/plugins/access-policy/index.js +22 -0
- package/plugins/access-policy/index.js.map +1 -0
- package/plugins/access-policy/policy-guard-generator.d.ts +22 -0
- package/plugins/access-policy/policy-guard-generator.js +634 -0
- package/plugins/access-policy/policy-guard-generator.js.map +1 -0
- package/plugins/model-meta/index.d.ts +4 -0
- package/plugins/model-meta/index.js +232 -0
- package/plugins/model-meta/index.js.map +1 -0
- package/plugins/plugin-utils.d.ts +17 -0
- package/plugins/plugin-utils.js +80 -0
- package/plugins/plugin-utils.js.map +1 -0
- package/plugins/prisma/indent-string.d.ts +4 -0
- package/plugins/prisma/indent-string.js +12 -0
- package/plugins/prisma/indent-string.js.map +1 -0
- package/plugins/prisma/index.d.ts +4 -0
- package/plugins/prisma/index.js +22 -0
- package/plugins/prisma/index.js.map +1 -0
- package/plugins/prisma/prisma-builder.d.ts +145 -0
- package/plugins/prisma/prisma-builder.js +358 -0
- package/plugins/prisma/prisma-builder.js.map +1 -0
- package/plugins/prisma/schema-generator.d.ts +29 -0
- package/plugins/prisma/schema-generator.js +336 -0
- package/plugins/prisma/schema-generator.js.map +1 -0
- package/plugins/prisma/zmodel-code-generator.d.ts +30 -0
- package/plugins/prisma/zmodel-code-generator.js +124 -0
- package/plugins/prisma/zmodel-code-generator.js.map +1 -0
- package/plugins/zod/generator.d.ts +4 -0
- package/plugins/zod/generator.js +254 -0
- package/plugins/zod/generator.js.map +1 -0
- package/plugins/zod/index.d.ts +4 -0
- package/plugins/zod/index.js +24 -0
- package/plugins/zod/index.js.map +1 -0
- package/plugins/zod/transformer.d.ts +68 -0
- package/plugins/zod/transformer.js +554 -0
- package/plugins/zod/transformer.js.map +1 -0
- package/plugins/zod/types.d.ts +25 -0
- package/plugins/zod/types.js +3 -0
- package/plugins/zod/types.js.map +1 -0
- package/plugins/zod/utils/removeDir.d.ts +1 -0
- package/plugins/zod/utils/removeDir.js +30 -0
- package/plugins/zod/utils/removeDir.js.map +1 -0
- package/plugins/zod/utils/schema-gen.d.ts +3 -0
- package/plugins/zod/utils/schema-gen.js +188 -0
- package/plugins/zod/utils/schema-gen.js.map +1 -0
- package/res/prism-zmodel.js +20 -0
- package/res/starter.zmodel +51 -0
- package/res/stdlib.zmodel +506 -0
- package/telemetry.d.ts +21 -0
- package/telemetry.js +129 -0
- package/telemetry.js.map +1 -0
- package/utils/ast-utils.d.ts +13 -0
- package/utils/ast-utils.js +136 -0
- package/utils/ast-utils.js.map +1 -0
- package/utils/exec-utils.d.ts +6 -0
- package/utils/exec-utils.js +13 -0
- package/utils/exec-utils.js.map +1 -0
- package/utils/pkg-utils.d.ts +3 -0
- package/utils/pkg-utils.js +64 -0
- package/utils/pkg-utils.js.map +1 -0
- package/utils/typescript-expression-transformer.d.ts +54 -0
- package/utils/typescript-expression-transformer.js +326 -0
- package/utils/typescript-expression-transformer.js.map +1 -0
- package/utils/version-utils.d.ts +1 -0
- package/utils/version-utils.js +20 -0
- package/utils/version-utils.js.map +1 -0
- package/.vscode/extensions.json +0 -7
- package/.vscode/launch.json +0 -49
- package/.vscode/settings.json +0 -4
- package/packages/internal/jest.config.ts +0 -32
- package/packages/internal/package.json +0 -42
- package/packages/internal/src/constants.ts +0 -1
- package/packages/internal/src/handler/data/guard-utils.ts +0 -7
- package/packages/internal/src/handler/data/handler.ts +0 -415
- package/packages/internal/src/handler/data/query-processor.ts +0 -504
- package/packages/internal/src/handler/index.ts +0 -1
- package/packages/internal/src/handler/types.ts +0 -20
- package/packages/internal/src/index.ts +0 -3
- package/packages/internal/src/request-handler.ts +0 -27
- package/packages/internal/src/request.ts +0 -101
- package/packages/internal/src/types.ts +0 -40
- package/packages/internal/tests/query-processor.test.ts +0 -172
- package/packages/internal/tsconfig.json +0 -21
- package/packages/runtime/auth.d.ts +0 -1
- package/packages/runtime/auth.js +0 -3
- package/packages/runtime/hooks.d.ts +0 -10
- package/packages/runtime/hooks.js +0 -3
- package/packages/runtime/index.d.ts +0 -3
- package/packages/runtime/index.js +0 -1
- package/packages/runtime/package-lock.json +0 -512
- package/packages/runtime/package.json +0 -16
- package/packages/runtime/server.d.ts +0 -1
- package/packages/runtime/server.js +0 -3
- package/packages/runtime/types.d.ts +0 -1
- package/packages/runtime/types.js +0 -3
- package/packages/schema/.eslintrc.json +0 -13
- package/packages/schema/.vscodeignore +0 -4
- package/packages/schema/asset/logo-dark.png +0 -0
- package/packages/schema/asset/logo-light.png +0 -0
- package/packages/schema/bin/cli +0 -3
- package/packages/schema/jest.config.ts +0 -32
- package/packages/schema/langium-config.json +0 -14
- package/packages/schema/langium-quickstart.md +0 -41
- package/packages/schema/language-configuration.json +0 -30
- package/packages/schema/package.json +0 -96
- package/packages/schema/src/cli/cli-util.ts +0 -80
- package/packages/schema/src/cli/index.ts +0 -64
- package/packages/schema/src/extension.ts +0 -76
- package/packages/schema/src/generator/constants.ts +0 -5
- package/packages/schema/src/generator/index.ts +0 -92
- package/packages/schema/src/generator/next-auth/index.ts +0 -197
- package/packages/schema/src/generator/package.template.json +0 -9
- package/packages/schema/src/generator/prisma/expression-writer.ts +0 -352
- package/packages/schema/src/generator/prisma/index.ts +0 -32
- package/packages/schema/src/generator/prisma/plain-expression-builder.ts +0 -91
- package/packages/schema/src/generator/prisma/prisma-builder.ts +0 -366
- package/packages/schema/src/generator/prisma/query-gard-generator.ts +0 -208
- package/packages/schema/src/generator/prisma/schema-generator.ts +0 -300
- package/packages/schema/src/generator/react-hooks/index.ts +0 -181
- package/packages/schema/src/generator/service/index.ts +0 -107
- package/packages/schema/src/generator/tsconfig.template.json +0 -17
- package/packages/schema/src/generator/types.ts +0 -17
- package/packages/schema/src/generator/utils.ts +0 -9
- package/packages/schema/src/language-server/generated/ast.ts +0 -603
- package/packages/schema/src/language-server/generated/grammar.ts +0 -2190
- package/packages/schema/src/language-server/generated/module.ts +0 -24
- package/packages/schema/src/language-server/main.ts +0 -12
- package/packages/schema/src/language-server/stdlib.zmodel +0 -22
- package/packages/schema/src/language-server/types.ts +0 -9
- package/packages/schema/src/language-server/zmodel-index.ts +0 -33
- package/packages/schema/src/language-server/zmodel-linker.ts +0 -409
- package/packages/schema/src/language-server/zmodel-module.ts +0 -90
- package/packages/schema/src/language-server/zmodel-scope.ts +0 -21
- package/packages/schema/src/language-server/zmodel-validator.ts +0 -35
- package/packages/schema/src/language-server/zmodel.langium +0 -186
- package/packages/schema/src/utils/exec-utils.ts +0 -5
- package/packages/schema/src/utils/indent-string.ts +0 -6
- package/packages/schema/syntaxes/zmodel.json +0 -57
- package/packages/schema/syntaxes/zmodel.tmLanguage.json +0 -57
- package/packages/schema/tests/generator/expression-writer.test.ts +0 -676
- package/packages/schema/tests/generator/prisma-builder.test.ts +0 -138
- package/packages/schema/tests/schema/parser.test.ts +0 -423
- package/packages/schema/tests/schema/sample-todo.test.ts +0 -14
- package/packages/schema/tests/utils.ts +0 -38
- package/packages/schema/tsconfig.json +0 -23
- package/pnpm-workspace.yaml +0 -3
- package/samples/todo/.env +0 -2
- package/samples/todo/.eslintrc.json +0 -3
- package/samples/todo/.vscode/launch.json +0 -11
- package/samples/todo/README.md +0 -34
- package/samples/todo/components/AuthGuard.tsx +0 -17
- package/samples/todo/components/Avatar.tsx +0 -22
- package/samples/todo/components/BreadCrumb.tsx +0 -44
- package/samples/todo/components/ManageMembers.tsx +0 -134
- package/samples/todo/components/NavBar.tsx +0 -57
- package/samples/todo/components/SpaceMembers.tsx +0 -76
- package/samples/todo/components/Spaces.tsx +0 -28
- package/samples/todo/components/TimeInfo.tsx +0 -17
- package/samples/todo/components/Todo.tsx +0 -72
- package/samples/todo/components/TodoList.tsx +0 -77
- package/samples/todo/lib/context.ts +0 -31
- package/samples/todo/next.config.js +0 -10
- package/samples/todo/package-lock.json +0 -7527
- package/samples/todo/package.json +0 -45
- package/samples/todo/pages/_app.tsx +0 -50
- package/samples/todo/pages/api/auth/[...nextauth].ts +0 -83
- package/samples/todo/pages/api/zenstack/[...path].ts +0 -16
- package/samples/todo/pages/create-space.tsx +0 -114
- package/samples/todo/pages/index.tsx +0 -32
- package/samples/todo/pages/space/[slug]/[listId]/index.tsx +0 -88
- package/samples/todo/pages/space/[slug]/index.tsx +0 -169
- package/samples/todo/postcss.config.js +0 -6
- package/samples/todo/public/avatar.jpg +0 -0
- package/samples/todo/public/favicon.ico +0 -0
- package/samples/todo/public/logo.png +0 -0
- package/samples/todo/public/vercel.svg +0 -4
- package/samples/todo/styles/globals.css +0 -7
- package/samples/todo/tailwind.config.js +0 -11
- package/samples/todo/tsconfig.json +0 -28
- package/samples/todo/types/next-auth.d.ts +0 -14
- package/samples/todo/types/next.d.ts +0 -16
- package/samples/todo/zenstack/migrations/20221014084317_init/migration.sql +0 -153
- package/samples/todo/zenstack/migrations/20221020094651_upate_cli/migration.sql +0 -23
- package/samples/todo/zenstack/migrations/migration_lock.toml +0 -3
- package/samples/todo/zenstack/schema.prisma +0 -126
- package/samples/todo/zenstack/schema.zmodel +0 -161
- package/tests/integration/jest.config.ts +0 -16
- package/tests/integration/package-lock.json +0 -1081
- package/tests/integration/package.json +0 -27
- package/tests/integration/tests/operation-coverate.test.ts +0 -563
- package/tests/integration/tests/operations.zmodel +0 -69
- package/tests/integration/tests/todo-e2e.test.ts +0 -577
- package/tests/integration/tests/todo.zmodel +0 -123
- package/tests/integration/tests/tsconfig.template.json +0 -10
- package/tests/integration/tests/utils.ts +0 -133
- package/tests/integration/tsconfig.json +0 -10
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const ast_1 = require("@zenstackhq/language/ast");
|
|
16
|
+
const runtime_1 = require("@zenstackhq/runtime");
|
|
17
|
+
const sdk_1 = require("@zenstackhq/sdk");
|
|
18
|
+
const langium_1 = require("langium");
|
|
19
|
+
const lower_case_first_1 = require("lower-case-first");
|
|
20
|
+
const path_1 = __importDefault(require("path"));
|
|
21
|
+
const ts_morph_1 = require("ts-morph");
|
|
22
|
+
const _1 = require(".");
|
|
23
|
+
const ast_utils_1 = require("../../utils/ast-utils");
|
|
24
|
+
const typescript_expression_transformer_1 = require("../../utils/typescript-expression-transformer");
|
|
25
|
+
const plugin_utils_1 = require("../plugin-utils");
|
|
26
|
+
const expression_writer_1 = require("./expression-writer");
|
|
27
|
+
/**
|
|
28
|
+
* Generates source file that contains Prisma query guard objects used for injecting database queries
|
|
29
|
+
*/
|
|
30
|
+
class PolicyGenerator {
|
|
31
|
+
generate(model, options, globalOptions) {
|
|
32
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
33
|
+
let output = options.output ? options.output : (0, plugin_utils_1.getDefaultOutputFolder)(globalOptions);
|
|
34
|
+
if (!output) {
|
|
35
|
+
throw new sdk_1.PluginError(options.name, `Unable to determine output path, not running plugin`);
|
|
36
|
+
}
|
|
37
|
+
output = (0, sdk_1.resolvePath)(output, options);
|
|
38
|
+
const project = (0, sdk_1.createProject)();
|
|
39
|
+
const sf = project.createSourceFile(path_1.default.join(output, 'policy.ts'), undefined, { overwrite: true });
|
|
40
|
+
sf.addStatements('/* eslint-disable */');
|
|
41
|
+
sf.addImportDeclaration({
|
|
42
|
+
namedImports: [
|
|
43
|
+
{ name: 'type QueryContext' },
|
|
44
|
+
{ name: 'type DbOperations' },
|
|
45
|
+
{ name: 'hasAllFields' },
|
|
46
|
+
{ name: 'allFieldsEqual' },
|
|
47
|
+
{ name: 'type PolicyDef' },
|
|
48
|
+
],
|
|
49
|
+
moduleSpecifier: `${sdk_1.RUNTIME_PACKAGE}`,
|
|
50
|
+
});
|
|
51
|
+
// import enums
|
|
52
|
+
const prismaImport = (0, sdk_1.getPrismaClientImportSpec)(model, output);
|
|
53
|
+
for (const e of model.declarations.filter((d) => (0, ast_1.isEnum)(d) && this.isEnumReferenced(model, d))) {
|
|
54
|
+
sf.addImportDeclaration({
|
|
55
|
+
namedImports: [{ name: e.name }],
|
|
56
|
+
moduleSpecifier: prismaImport,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const models = (0, sdk_1.getDataModels)(model);
|
|
60
|
+
const policyMap = {};
|
|
61
|
+
for (const model of models) {
|
|
62
|
+
policyMap[model.name] = yield this.generateQueryGuardForModel(model, sf);
|
|
63
|
+
}
|
|
64
|
+
sf.addVariableStatement({
|
|
65
|
+
declarationKind: ts_morph_1.VariableDeclarationKind.Const,
|
|
66
|
+
declarations: [
|
|
67
|
+
{
|
|
68
|
+
name: 'policy',
|
|
69
|
+
type: 'PolicyDef',
|
|
70
|
+
initializer: (writer) => {
|
|
71
|
+
writer.block(() => {
|
|
72
|
+
writer.write('guard:');
|
|
73
|
+
writer.inlineBlock(() => {
|
|
74
|
+
for (const [model, map] of Object.entries(policyMap)) {
|
|
75
|
+
writer.write(`${(0, lower_case_first_1.lowerCaseFirst)(model)}:`);
|
|
76
|
+
writer.inlineBlock(() => {
|
|
77
|
+
for (const [op, func] of Object.entries(map)) {
|
|
78
|
+
if (typeof func === 'object') {
|
|
79
|
+
writer.write(`${op}: ${JSON.stringify(func)},`);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
writer.write(`${op}: ${func},`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
writer.write(',');
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
writer.writeLine(',');
|
|
90
|
+
writer.write('validation:');
|
|
91
|
+
writer.inlineBlock(() => {
|
|
92
|
+
for (const model of models) {
|
|
93
|
+
writer.write(`${(0, lower_case_first_1.lowerCaseFirst)(model.name)}:`);
|
|
94
|
+
writer.inlineBlock(() => {
|
|
95
|
+
writer.write(`hasValidation: ${(0, sdk_1.hasValidationAttributes)(model)}`);
|
|
96
|
+
});
|
|
97
|
+
writer.writeLine(',');
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
sf.addStatements('export default policy');
|
|
106
|
+
let shouldCompile = true;
|
|
107
|
+
if (typeof options.compile === 'boolean') {
|
|
108
|
+
// explicit override
|
|
109
|
+
shouldCompile = options.compile;
|
|
110
|
+
}
|
|
111
|
+
else if (globalOptions) {
|
|
112
|
+
shouldCompile = globalOptions.compile;
|
|
113
|
+
}
|
|
114
|
+
if (!shouldCompile || options.preserveTsFiles === true) {
|
|
115
|
+
// save ts files
|
|
116
|
+
yield (0, sdk_1.saveProject)(project);
|
|
117
|
+
}
|
|
118
|
+
if (shouldCompile) {
|
|
119
|
+
yield (0, sdk_1.emitProject)(project);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
isEnumReferenced(model, decl) {
|
|
124
|
+
return (0, langium_1.streamAllContents)(model).some((node) => {
|
|
125
|
+
var _a, _b;
|
|
126
|
+
if ((0, ast_1.isDataModelField)(node) && ((_a = node.type.reference) === null || _a === void 0 ? void 0 : _a.ref) === decl) {
|
|
127
|
+
// referenced as field type
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
if ((0, sdk_1.isEnumFieldReference)(node) && ((_b = node.target.ref) === null || _b === void 0 ? void 0 : _b.$container) === decl) {
|
|
131
|
+
// enum field is referenced
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
getPolicyExpressions(target, kind, operation) {
|
|
138
|
+
const attributes = target.attributes;
|
|
139
|
+
const attrName = (0, ast_1.isDataModel)(target) ? `@@${kind}` : `@${kind}`;
|
|
140
|
+
const attrs = attributes.filter((attr) => { var _a; return ((_a = attr.decl.ref) === null || _a === void 0 ? void 0 : _a.name) === attrName; });
|
|
141
|
+
const checkOperation = operation === 'postUpdate' ? 'update' : operation;
|
|
142
|
+
let result = attrs
|
|
143
|
+
.filter((attr) => {
|
|
144
|
+
const opsValue = (0, sdk_1.getLiteral)(attr.args[0].value);
|
|
145
|
+
if (!opsValue) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
const ops = opsValue.split(',').map((s) => s.trim());
|
|
149
|
+
return ops.includes(checkOperation) || ops.includes('all');
|
|
150
|
+
})
|
|
151
|
+
.map((attr) => attr.args[1].value);
|
|
152
|
+
if (operation === 'update') {
|
|
153
|
+
result = this.processUpdatePolicies(result, false);
|
|
154
|
+
}
|
|
155
|
+
else if (operation === 'postUpdate') {
|
|
156
|
+
result = this.processUpdatePolicies(result, true);
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
processUpdatePolicies(expressions, postUpdate) {
|
|
161
|
+
return expressions
|
|
162
|
+
.map((expr) => this.visitPolicyExpression(expr, postUpdate))
|
|
163
|
+
.filter((e) => !!e);
|
|
164
|
+
}
|
|
165
|
+
visitPolicyExpression(expr, postUpdate) {
|
|
166
|
+
if ((0, ast_1.isBinaryExpr)(expr) && (expr.operator === '&&' || expr.operator === '||')) {
|
|
167
|
+
const left = this.visitPolicyExpression(expr.left, postUpdate);
|
|
168
|
+
const right = this.visitPolicyExpression(expr.right, postUpdate);
|
|
169
|
+
if (!left)
|
|
170
|
+
return right;
|
|
171
|
+
if (!right)
|
|
172
|
+
return left;
|
|
173
|
+
return Object.assign(Object.assign({}, expr), { left, right });
|
|
174
|
+
}
|
|
175
|
+
if ((0, ast_1.isUnaryExpr)(expr) && expr.operator === '!') {
|
|
176
|
+
const operand = this.visitPolicyExpression(expr.operand, postUpdate);
|
|
177
|
+
if (!operand)
|
|
178
|
+
return undefined;
|
|
179
|
+
return Object.assign(Object.assign({}, expr), { operand });
|
|
180
|
+
}
|
|
181
|
+
if (postUpdate && !this.hasFutureReference(expr)) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
else if (!postUpdate && this.hasFutureReference(expr)) {
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
return expr;
|
|
188
|
+
}
|
|
189
|
+
hasFutureReference(expr) {
|
|
190
|
+
var _a;
|
|
191
|
+
for (const node of (0, langium_1.streamAst)(expr)) {
|
|
192
|
+
if ((0, ast_1.isInvocationExpr)(node) && ((_a = node.function.ref) === null || _a === void 0 ? void 0 : _a.name) === 'future' && (0, sdk_1.isFromStdlib)(node.function.ref)) {
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
generateQueryGuardForModel(model, sourceFile) {
|
|
199
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
200
|
+
const result = {};
|
|
201
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
202
|
+
const policies = (0, sdk_1.analyzePolicies)(model);
|
|
203
|
+
for (const kind of plugin_utils_1.ALL_OPERATION_KINDS) {
|
|
204
|
+
if (policies[kind] === true || policies[kind] === false) {
|
|
205
|
+
result[kind] = policies[kind];
|
|
206
|
+
if (kind === 'create') {
|
|
207
|
+
result[kind + '_input'] = policies[kind];
|
|
208
|
+
}
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const denies = this.getPolicyExpressions(model, 'deny', kind);
|
|
212
|
+
const allows = this.getPolicyExpressions(model, 'allow', kind);
|
|
213
|
+
if (kind === 'update' && allows.length === 0) {
|
|
214
|
+
// no allow rule for 'update', policy is constant based on if there's
|
|
215
|
+
// post-update counterpart
|
|
216
|
+
if (this.getPolicyExpressions(model, 'allow', 'postUpdate').length === 0) {
|
|
217
|
+
result[kind] = false;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
result[kind] = true;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (kind === 'postUpdate' && allows.length === 0 && denies.length === 0) {
|
|
226
|
+
// no rule 'postUpdate', always allow
|
|
227
|
+
result[kind] = true;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const guardFunc = this.generateQueryGuardFunction(sourceFile, model, kind, allows, denies);
|
|
231
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
232
|
+
result[kind] = guardFunc.getName();
|
|
233
|
+
if (kind === 'postUpdate') {
|
|
234
|
+
const preValueSelect = this.generateSelectForRules(allows, denies);
|
|
235
|
+
if (preValueSelect) {
|
|
236
|
+
result[runtime_1.PRE_UPDATE_VALUE_SELECTOR] = preValueSelect;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (kind === 'create' && this.canCheckCreateBasedOnInput(model, allows, denies)) {
|
|
240
|
+
const inputCheckFunc = this.generateInputCheckFunction(sourceFile, model, kind, allows, denies);
|
|
241
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
242
|
+
result[kind + '_input'] = inputCheckFunc.getName();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// generate field read checkers
|
|
246
|
+
this.generateReadFieldsGuards(model, sourceFile, result);
|
|
247
|
+
// generate field update guards
|
|
248
|
+
this.generateUpdateFieldsGuards(model, sourceFile, result);
|
|
249
|
+
return result;
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
generateReadFieldsGuards(model, sourceFile, result) {
|
|
253
|
+
const allFieldsAllows = [];
|
|
254
|
+
const allFieldsDenies = [];
|
|
255
|
+
for (const field of model.fields) {
|
|
256
|
+
const allows = this.getPolicyExpressions(field, 'allow', 'read');
|
|
257
|
+
const denies = this.getPolicyExpressions(field, 'deny', 'read');
|
|
258
|
+
if (denies.length === 0 && allows.length === 0) {
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
allFieldsAllows.push(...allows);
|
|
262
|
+
allFieldsDenies.push(...denies);
|
|
263
|
+
const guardFunc = this.generateReadFieldGuardFunction(sourceFile, field, allows, denies);
|
|
264
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
265
|
+
result[`${runtime_1.FIELD_LEVEL_READ_CHECKER_PREFIX}${field.name}`] = guardFunc.getName();
|
|
266
|
+
}
|
|
267
|
+
if (allFieldsAllows.length > 0 || allFieldsDenies.length > 0) {
|
|
268
|
+
result[runtime_1.HAS_FIELD_LEVEL_POLICY_FLAG] = true;
|
|
269
|
+
const readFieldCheckSelect = this.generateSelectForRules(allFieldsAllows, allFieldsDenies);
|
|
270
|
+
if (readFieldCheckSelect) {
|
|
271
|
+
result[runtime_1.FIELD_LEVEL_READ_CHECKER_SELECTOR] = readFieldCheckSelect;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
generateReadFieldGuardFunction(sourceFile, field, allows, denies) {
|
|
276
|
+
const statements = [];
|
|
277
|
+
this.generateNormalizedAuthRef(field.$container, allows, denies, statements);
|
|
278
|
+
// compile rules down to typescript expressions
|
|
279
|
+
statements.push((writer) => {
|
|
280
|
+
const transformer = new typescript_expression_transformer_1.TypeScriptExpressionTransformer({
|
|
281
|
+
context: sdk_1.ExpressionContext.AccessPolicy,
|
|
282
|
+
fieldReferenceContext: 'input',
|
|
283
|
+
});
|
|
284
|
+
const denyStmt = denies.length > 0
|
|
285
|
+
? '!(' +
|
|
286
|
+
denies
|
|
287
|
+
.map((deny) => {
|
|
288
|
+
return transformer.transform(deny);
|
|
289
|
+
})
|
|
290
|
+
.join(' || ') +
|
|
291
|
+
')'
|
|
292
|
+
: undefined;
|
|
293
|
+
const allowStmt = allows.length > 0
|
|
294
|
+
? '(' +
|
|
295
|
+
allows
|
|
296
|
+
.map((allow) => {
|
|
297
|
+
return transformer.transform(allow);
|
|
298
|
+
})
|
|
299
|
+
.join(' || ') +
|
|
300
|
+
')'
|
|
301
|
+
: undefined;
|
|
302
|
+
let expr;
|
|
303
|
+
if (denyStmt && allowStmt) {
|
|
304
|
+
expr = `${denyStmt} && ${allowStmt}`;
|
|
305
|
+
}
|
|
306
|
+
else if (denyStmt) {
|
|
307
|
+
expr = denyStmt;
|
|
308
|
+
}
|
|
309
|
+
else if (allowStmt) {
|
|
310
|
+
expr = allowStmt;
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
throw new Error('should not happen');
|
|
314
|
+
}
|
|
315
|
+
writer.write('return ' + expr);
|
|
316
|
+
});
|
|
317
|
+
const func = sourceFile.addFunction({
|
|
318
|
+
name: `${field.$container.name}$${field.name}_read`,
|
|
319
|
+
returnType: 'boolean',
|
|
320
|
+
parameters: [
|
|
321
|
+
{
|
|
322
|
+
name: 'input',
|
|
323
|
+
type: 'any',
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
name: 'context',
|
|
327
|
+
type: 'QueryContext',
|
|
328
|
+
},
|
|
329
|
+
],
|
|
330
|
+
statements,
|
|
331
|
+
});
|
|
332
|
+
return func;
|
|
333
|
+
}
|
|
334
|
+
generateUpdateFieldsGuards(model, sourceFile, result) {
|
|
335
|
+
for (const field of model.fields) {
|
|
336
|
+
const allows = this.getPolicyExpressions(field, 'allow', 'update');
|
|
337
|
+
const denies = this.getPolicyExpressions(field, 'deny', 'update');
|
|
338
|
+
if (denies.length === 0 && allows.length === 0) {
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
const guardFunc = this.generateQueryGuardFunction(sourceFile, model, 'update', allows, denies, field);
|
|
342
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
343
|
+
result[`${runtime_1.FIELD_LEVEL_UPDATE_GUARD_PREFIX}${field.name}`] = guardFunc.getName();
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
canCheckCreateBasedOnInput(model, allows, denies) {
|
|
347
|
+
return [...allows, ...denies].every((rule) => {
|
|
348
|
+
return (0, langium_1.streamAst)(rule).every((expr) => {
|
|
349
|
+
var _a;
|
|
350
|
+
if ((0, ast_1.isThisExpr)(expr)) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
if ((0, ast_1.isReferenceExpr)(expr)) {
|
|
354
|
+
if ((0, ast_1.isDataModel)((_a = expr.$resolvedType) === null || _a === void 0 ? void 0 : _a.decl)) {
|
|
355
|
+
// if policy rules uses relation fields,
|
|
356
|
+
// we can't check based on create input
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
if ((0, ast_1.isDataModelField)(expr.target.ref) &&
|
|
360
|
+
expr.target.ref.$container === model &&
|
|
361
|
+
(0, sdk_1.hasAttribute)(expr.target.ref, '@default')) {
|
|
362
|
+
// reference to field of current model
|
|
363
|
+
// if it has default value, we can't check
|
|
364
|
+
// based on create input
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
if ((0, ast_1.isDataModelField)(expr.target.ref) && (0, sdk_1.isForeignKeyField)(expr.target.ref)) {
|
|
368
|
+
// reference to foreign key field
|
|
369
|
+
// we can't check based on create input
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return true;
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
// generates a "select" object that contains (recursively) fields referenced by the
|
|
378
|
+
// given policy rules
|
|
379
|
+
generateSelectForRules(allows, denies) {
|
|
380
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
381
|
+
const result = {};
|
|
382
|
+
const addPath = (path) => {
|
|
383
|
+
let curr = result;
|
|
384
|
+
path.forEach((seg, i) => {
|
|
385
|
+
if (i === path.length - 1) {
|
|
386
|
+
curr[seg] = true;
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
if (!curr[seg]) {
|
|
390
|
+
curr[seg] = { select: {} };
|
|
391
|
+
}
|
|
392
|
+
curr = curr[seg].select;
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
};
|
|
396
|
+
// visit a reference or member access expression to build a
|
|
397
|
+
// selection path
|
|
398
|
+
const visit = (node) => {
|
|
399
|
+
if ((0, ast_1.isReferenceExpr)(node)) {
|
|
400
|
+
const target = (0, sdk_1.resolved)(node.target);
|
|
401
|
+
if ((0, ast_1.isDataModelField)(target)) {
|
|
402
|
+
// a field selection, it's a terminal
|
|
403
|
+
return [target.name];
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
else if ((0, ast_1.isMemberAccessExpr)(node)) {
|
|
407
|
+
if ((0, sdk_1.isFutureExpr)(node.operand)) {
|
|
408
|
+
// future().field is not subject to pre-update select
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
411
|
+
// build a selection path inside-out for chained member access
|
|
412
|
+
const inner = visit(node.operand);
|
|
413
|
+
if (inner) {
|
|
414
|
+
return [...inner, node.member.$refText];
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return undefined;
|
|
418
|
+
};
|
|
419
|
+
// collect selection paths from the given expression
|
|
420
|
+
const collectReferencePaths = (expr) => {
|
|
421
|
+
var _a, _b, _c;
|
|
422
|
+
if ((0, ast_1.isThisExpr)(expr) && !(0, ast_1.isMemberAccessExpr)(expr.$container)) {
|
|
423
|
+
// a standalone `this` expression, include all id fields
|
|
424
|
+
const model = (_a = expr.$resolvedType) === null || _a === void 0 ? void 0 : _a.decl;
|
|
425
|
+
const idFields = (0, ast_utils_1.getIdFields)(model);
|
|
426
|
+
return idFields.map((field) => [field.name]);
|
|
427
|
+
}
|
|
428
|
+
if ((0, ast_1.isMemberAccessExpr)(expr) || (0, ast_1.isReferenceExpr)(expr)) {
|
|
429
|
+
const path = visit(expr);
|
|
430
|
+
if (path) {
|
|
431
|
+
if ((0, ast_1.isDataModel)((_b = expr.$resolvedType) === null || _b === void 0 ? void 0 : _b.decl)) {
|
|
432
|
+
// member selection ended at a data model field, include its id fields
|
|
433
|
+
const idFields = (0, ast_utils_1.getIdFields)((_c = expr.$resolvedType) === null || _c === void 0 ? void 0 : _c.decl);
|
|
434
|
+
return idFields.map((field) => [...path, field.name]);
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
return [path];
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
return [];
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
else if ((0, ast_utils_1.isCollectionPredicate)(expr)) {
|
|
445
|
+
const path = visit(expr.left);
|
|
446
|
+
if (path) {
|
|
447
|
+
// recurse into RHS
|
|
448
|
+
const rhs = collectReferencePaths(expr.right);
|
|
449
|
+
// combine path of LHS and RHS
|
|
450
|
+
return rhs.map((r) => [...path, ...r]);
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
return [];
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
// recurse
|
|
458
|
+
const children = (0, langium_1.streamContents)(expr)
|
|
459
|
+
.filter((child) => (0, ast_1.isExpression)(child))
|
|
460
|
+
.toArray();
|
|
461
|
+
return children.flatMap((child) => collectReferencePaths(child));
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
for (const rule of [...allows, ...denies]) {
|
|
465
|
+
const paths = collectReferencePaths(rule);
|
|
466
|
+
paths.forEach((p) => addPath(p));
|
|
467
|
+
}
|
|
468
|
+
return Object.keys(result).length === 0 ? undefined : result;
|
|
469
|
+
}
|
|
470
|
+
generateQueryGuardFunction(sourceFile, model, kind, allows, denies, forField) {
|
|
471
|
+
const statements = [];
|
|
472
|
+
this.generateNormalizedAuthRef(model, allows, denies, statements);
|
|
473
|
+
const hasFieldAccess = [...denies, ...allows].some((rule) => (0, langium_1.streamAst)(rule).some((child) =>
|
|
474
|
+
// this.???
|
|
475
|
+
(0, ast_1.isThisExpr)(child) ||
|
|
476
|
+
// future().???
|
|
477
|
+
(0, sdk_1.isFutureExpr)(child) ||
|
|
478
|
+
// field reference
|
|
479
|
+
((0, ast_1.isReferenceExpr)(child) && (0, ast_1.isDataModelField)(child.target.ref))));
|
|
480
|
+
if (!hasFieldAccess) {
|
|
481
|
+
// none of the rules reference model fields, we can compile down to a plain boolean
|
|
482
|
+
// function in this case (so we can skip doing SQL queries when validating)
|
|
483
|
+
statements.push((writer) => {
|
|
484
|
+
const transformer = new typescript_expression_transformer_1.TypeScriptExpressionTransformer({
|
|
485
|
+
context: sdk_1.ExpressionContext.AccessPolicy,
|
|
486
|
+
isPostGuard: kind === 'postUpdate',
|
|
487
|
+
});
|
|
488
|
+
try {
|
|
489
|
+
denies.forEach((rule) => {
|
|
490
|
+
writer.write(`if (${transformer.transform(rule, false)}) { return ${expression_writer_1.FALSE}; }`);
|
|
491
|
+
});
|
|
492
|
+
allows.forEach((rule) => {
|
|
493
|
+
writer.write(`if (${transformer.transform(rule, false)}) { return ${expression_writer_1.TRUE}; }`);
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
catch (err) {
|
|
497
|
+
if (err instanceof typescript_expression_transformer_1.TypeScriptExpressionTransformerError) {
|
|
498
|
+
throw new sdk_1.PluginError(_1.name, err.message);
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
throw err;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
writer.write(`return ${expression_writer_1.FALSE};`);
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
statements.push((writer) => {
|
|
509
|
+
writer.write('return ');
|
|
510
|
+
const exprWriter = new expression_writer_1.ExpressionWriter(writer, kind === 'postUpdate');
|
|
511
|
+
const writeDenies = () => {
|
|
512
|
+
writer.conditionalWrite(denies.length > 1, '{ AND: [');
|
|
513
|
+
denies.forEach((expr, i) => {
|
|
514
|
+
writer.inlineBlock(() => {
|
|
515
|
+
writer.write('NOT: ');
|
|
516
|
+
exprWriter.write(expr);
|
|
517
|
+
});
|
|
518
|
+
writer.conditionalWrite(i !== denies.length - 1, ',');
|
|
519
|
+
});
|
|
520
|
+
writer.conditionalWrite(denies.length > 1, ']}');
|
|
521
|
+
};
|
|
522
|
+
const writeAllows = () => {
|
|
523
|
+
writer.conditionalWrite(allows.length > 1, '{ OR: [');
|
|
524
|
+
allows.forEach((expr, i) => {
|
|
525
|
+
exprWriter.write(expr);
|
|
526
|
+
writer.conditionalWrite(i !== allows.length - 1, ',');
|
|
527
|
+
});
|
|
528
|
+
writer.conditionalWrite(allows.length > 1, ']}');
|
|
529
|
+
};
|
|
530
|
+
if (allows.length > 0 && denies.length > 0) {
|
|
531
|
+
writer.write('{ AND: [');
|
|
532
|
+
writeDenies();
|
|
533
|
+
writer.write(',');
|
|
534
|
+
writeAllows();
|
|
535
|
+
writer.write(']}');
|
|
536
|
+
}
|
|
537
|
+
else if (denies.length > 0) {
|
|
538
|
+
writeDenies();
|
|
539
|
+
}
|
|
540
|
+
else if (allows.length > 0) {
|
|
541
|
+
writeAllows();
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
// disallow any operation
|
|
545
|
+
writer.write(`{ OR: [] }`);
|
|
546
|
+
}
|
|
547
|
+
writer.write(';');
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
const func = sourceFile.addFunction({
|
|
551
|
+
name: `${model.name}${forField ? '$' + forField.name : ''}_${kind}`,
|
|
552
|
+
returnType: 'any',
|
|
553
|
+
parameters: [
|
|
554
|
+
{
|
|
555
|
+
name: 'context',
|
|
556
|
+
type: 'QueryContext',
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
// for generating field references used by field comparison in the same model
|
|
560
|
+
name: 'db',
|
|
561
|
+
type: 'Record<string, DbOperations>',
|
|
562
|
+
},
|
|
563
|
+
],
|
|
564
|
+
statements,
|
|
565
|
+
});
|
|
566
|
+
return func;
|
|
567
|
+
}
|
|
568
|
+
generateInputCheckFunction(sourceFile, model, kind, allows, denies) {
|
|
569
|
+
const statements = [];
|
|
570
|
+
this.generateNormalizedAuthRef(model, allows, denies, statements);
|
|
571
|
+
statements.push((writer) => {
|
|
572
|
+
if (allows.length === 0) {
|
|
573
|
+
writer.write('return false;');
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const transformer = new typescript_expression_transformer_1.TypeScriptExpressionTransformer({
|
|
577
|
+
context: sdk_1.ExpressionContext.AccessPolicy,
|
|
578
|
+
fieldReferenceContext: 'input',
|
|
579
|
+
});
|
|
580
|
+
let expr = denies.length > 0
|
|
581
|
+
? '!(' +
|
|
582
|
+
denies
|
|
583
|
+
.map((deny) => {
|
|
584
|
+
return transformer.transform(deny);
|
|
585
|
+
})
|
|
586
|
+
.join(' || ') +
|
|
587
|
+
')'
|
|
588
|
+
: undefined;
|
|
589
|
+
const allowStmt = allows
|
|
590
|
+
.map((allow) => {
|
|
591
|
+
return transformer.transform(allow);
|
|
592
|
+
})
|
|
593
|
+
.join(' || ');
|
|
594
|
+
expr = expr ? `${expr} && (${allowStmt})` : allowStmt;
|
|
595
|
+
writer.write('return ' + expr);
|
|
596
|
+
});
|
|
597
|
+
const func = sourceFile.addFunction({
|
|
598
|
+
name: model.name + '_' + kind + '_input',
|
|
599
|
+
returnType: 'boolean',
|
|
600
|
+
parameters: [
|
|
601
|
+
{
|
|
602
|
+
name: 'input',
|
|
603
|
+
type: 'any',
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
name: 'context',
|
|
607
|
+
type: 'QueryContext',
|
|
608
|
+
},
|
|
609
|
+
],
|
|
610
|
+
statements,
|
|
611
|
+
});
|
|
612
|
+
return func;
|
|
613
|
+
}
|
|
614
|
+
generateNormalizedAuthRef(model, allows, denies, statements) {
|
|
615
|
+
// check if any allow or deny rule contains 'auth()' invocation
|
|
616
|
+
const hasAuthRef = [...allows, ...denies].some((rule) => (0, langium_1.streamAst)(rule).some((child) => (0, ast_utils_1.isAuthInvocation)(child)));
|
|
617
|
+
if (hasAuthRef) {
|
|
618
|
+
const userModel = model.$container.declarations.find((decl) => (0, ast_1.isDataModel)(decl) && decl.name === 'User');
|
|
619
|
+
if (!userModel) {
|
|
620
|
+
throw new sdk_1.PluginError(_1.name, 'User model not found');
|
|
621
|
+
}
|
|
622
|
+
const userIdFields = (0, ast_utils_1.getIdFields)(userModel);
|
|
623
|
+
if (!userIdFields || userIdFields.length === 0) {
|
|
624
|
+
throw new sdk_1.PluginError(_1.name, 'User model does not have an id field');
|
|
625
|
+
}
|
|
626
|
+
// normalize user to null to avoid accidentally use undefined in filter
|
|
627
|
+
statements.push(`const user = hasAllFields(context.user, [${userIdFields
|
|
628
|
+
.map((f) => "'" + f.name + "'")
|
|
629
|
+
.join(', ')}]) ? context.user as any : null;`);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
exports.default = PolicyGenerator;
|
|
634
|
+
//# sourceMappingURL=policy-guard-generator.js.map
|