zenstack 0.6.0-pre.9 → 1.0.0-alpha.20
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/cli/cli-error.d.ts +5 -0
- package/cli/cli-error.js +12 -0
- package/cli/cli-error.js.map +1 -0
- package/cli/cli-util.d.ts +18 -0
- package/cli/cli-util.js +122 -0
- package/cli/cli-util.js.map +1 -0
- package/cli/index.d.ts +14 -0
- package/cli/index.js +72 -6529
- package/cli/index.js.map +1 -0
- package/cli/plugin-runner.d.ts +14 -0
- package/cli/plugin-runner.js +138 -0
- package/cli/plugin-runner.js.map +1 -0
- package/global.d.js +1 -0
- package/global.d.js.map +1 -0
- package/language-server/constants.d.ts +12 -0
- package/language-server/constants.js +24 -0
- package/language-server/constants.js.map +1 -0
- package/language-server/main.d.ts +1 -0
- package/language-server/main.js +20 -6436
- package/language-server/main.js.map +1 -0
- package/language-server/types.d.ts +10 -0
- package/language-server/types.js +1 -0
- package/language-server/types.js.map +1 -0
- package/language-server/utils.d.ts +10 -0
- package/language-server/utils.js +28 -0
- package/language-server/utils.js.map +1 -0
- package/language-server/validator/attribute-validator.d.ts +9 -0
- package/language-server/validator/attribute-validator.js +15 -0
- package/language-server/validator/attribute-validator.js.map +1 -0
- package/language-server/validator/datamodel-validator.d.ts +16 -0
- package/language-server/validator/datamodel-validator.js +365 -0
- package/language-server/validator/datamodel-validator.js.map +1 -0
- package/language-server/validator/datasource-validator.d.ts +13 -0
- package/language-server/validator/datasource-validator.js +85 -0
- package/language-server/validator/datasource-validator.js.map +1 -0
- package/language-server/validator/enum-validator.d.ts +9 -0
- package/language-server/validator/enum-validator.js +18 -0
- package/language-server/validator/enum-validator.js.map +1 -0
- package/language-server/validator/expression-validator.d.ts +11 -0
- package/language-server/validator/expression-validator.js +40 -0
- package/language-server/validator/expression-validator.js.map +1 -0
- package/language-server/validator/schema-validator.d.ts +10 -0
- package/language-server/validator/schema-validator.js +35 -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 +116 -0
- package/language-server/validator/utils.js.map +1 -0
- package/language-server/validator/zmodel-validator.d.ts +21 -0
- package/language-server/validator/zmodel-validator.js +72 -0
- package/language-server/validator/zmodel-validator.js.map +1 -0
- package/language-server/zmodel-linker.d.ts +29 -0
- package/language-server/zmodel-linker.js +364 -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 +10 -0
- package/language-server/zmodel-scope.js +34 -0
- package/language-server/zmodel-scope.js.map +1 -0
- package/language-server/zmodel-workspace-manager.d.ts +8 -0
- package/language-server/zmodel-workspace-manager.js +25 -0
- package/language-server/zmodel-workspace-manager.js.map +1 -0
- package/package.json +24 -18
- package/plugins/access-policy/expression-writer.d.ts +38 -0
- package/plugins/access-policy/expression-writer.js +323 -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 +15 -0
- package/plugins/access-policy/index.js.map +1 -0
- package/plugins/access-policy/policy-guard-generator.d.ts +15 -0
- package/plugins/access-policy/policy-guard-generator.js +345 -0
- package/plugins/access-policy/policy-guard-generator.js.map +1 -0
- package/plugins/access-policy/typescript-expression-transformer.d.ts +26 -0
- package/plugins/access-policy/typescript-expression-transformer.js +110 -0
- package/plugins/access-policy/typescript-expression-transformer.js.map +1 -0
- package/plugins/access-policy/utils.d.ts +5 -0
- package/plugins/access-policy/utils.js +16 -0
- package/plugins/access-policy/utils.js.map +1 -0
- package/plugins/access-policy/zod-schema-generator.d.ts +12 -0
- package/plugins/access-policy/zod-schema-generator.js +174 -0
- package/plugins/access-policy/zod-schema-generator.js.map +1 -0
- package/plugins/model-meta/index.d.ts +4 -0
- package/plugins/model-meta/index.js +130 -0
- package/plugins/model-meta/index.js.map +1 -0
- package/plugins/plugin-utils.d.ts +16 -0
- package/plugins/plugin-utils.js +58 -0
- package/plugins/plugin-utils.js.map +1 -0
- package/plugins/prisma/indent-string.d.ts +4 -0
- package/plugins/prisma/indent-string.js +16 -0
- package/plugins/prisma/indent-string.js.map +1 -0
- package/plugins/prisma/index.d.ts +4 -0
- package/plugins/prisma/index.js +15 -3740
- package/plugins/prisma/index.js.map +1 -0
- package/plugins/prisma/prisma-builder.d.ts +130 -0
- package/plugins/prisma/prisma-builder.js +271 -0
- package/plugins/prisma/prisma-builder.js.map +1 -0
- package/plugins/prisma/schema-generator.d.ts +24 -0
- package/plugins/prisma/schema-generator.js +212 -0
- package/plugins/prisma/schema-generator.js.map +1 -0
- package/plugins/prisma/zmodel-code-generator.d.ts +27 -0
- package/plugins/prisma/zmodel-code-generator.js +110 -0
- package/plugins/prisma/zmodel-code-generator.js.map +1 -0
- package/res/prism-zmodel.js +2 -4
- package/res/starter.zmodel +47 -0
- package/res/stdlib.zmodel +12 -1
- package/telemetry.d.ts +21 -0
- package/telemetry.js +102 -0
- package/telemetry.js.map +1 -0
- package/types.d.ts +11 -0
- package/types.js +1 -0
- package/types.js.map +1 -0
- package/utils/ast-utils.d.ts +15 -0
- package/utils/ast-utils.js +79 -0
- package/utils/ast-utils.js.map +1 -0
- package/utils/exec-utils.d.ts +6 -0
- package/utils/exec-utils.js +22 -0
- package/utils/exec-utils.js.map +1 -0
- package/utils/pkg-utils.d.ts +3 -0
- package/utils/pkg-utils.js +43 -0
- package/utils/pkg-utils.js.map +1 -0
- package/utils/version-utils.d.ts +1 -0
- package/utils/version-utils.js +15 -0
- package/utils/version-utils.js.map +1 -0
- package/asset/logo-256-bg.png +0 -0
- package/asset/logo-dark-256.png +0 -0
- package/asset/logo-light-256.png +0 -0
- package/extension.js +0 -39
- package/plugins/policy-guard/index.js +0 -4289
- package/plugins/react-hooks/index.js +0 -4318
- package/plugins/trpc/index.js +0 -5386
- package/plugins/zod/index.js +0 -4808
- package/res/package.template.json +0 -9
- package/res/tsconfig.template.json +0 -17
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _ast = require("@zenstackhq/language/ast");
|
|
8
|
+
var _sdk = require("@zenstackhq/sdk");
|
|
9
|
+
var _changeCase = require("change-case");
|
|
10
|
+
var _langium = require("langium");
|
|
11
|
+
var _path = _interopRequireDefault(require("path"));
|
|
12
|
+
var _tsMorph = require("ts-morph");
|
|
13
|
+
var _ = require(".");
|
|
14
|
+
var _utils = require("../../language-server/utils");
|
|
15
|
+
var _astUtils = require("../../utils/ast-utils");
|
|
16
|
+
var _pluginUtils = require("../plugin-utils");
|
|
17
|
+
var _expressionWriter = require("./expression-writer");
|
|
18
|
+
var _utils2 = require("./utils");
|
|
19
|
+
var _zodSchemaGenerator = require("./zod-schema-generator");
|
|
20
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
21
|
+
const UNKNOWN_USER_ID = 'zenstack_unknown_user';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Generates source file that contains Prisma query guard objects used for injecting database queries
|
|
25
|
+
*/
|
|
26
|
+
class PolicyGenerator {
|
|
27
|
+
async generate(model, options) {
|
|
28
|
+
const output = options.output ? options.output : (0, _pluginUtils.getDefaultOutputFolder)();
|
|
29
|
+
if (!output) {
|
|
30
|
+
console.error(`Unable to determine output path, not running plugin ${_.name}`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const project = new _tsMorph.Project();
|
|
34
|
+
const sf = project.createSourceFile(_path.default.join(output, 'policy.ts'), undefined, {
|
|
35
|
+
overwrite: true
|
|
36
|
+
});
|
|
37
|
+
sf.addImportDeclaration({
|
|
38
|
+
namedImports: [{
|
|
39
|
+
name: 'QueryContext'
|
|
40
|
+
}],
|
|
41
|
+
moduleSpecifier: `${_pluginUtils.RUNTIME_PACKAGE}`,
|
|
42
|
+
isTypeOnly: true
|
|
43
|
+
});
|
|
44
|
+
sf.addImportDeclaration({
|
|
45
|
+
namedImports: [{
|
|
46
|
+
name: 'z'
|
|
47
|
+
}],
|
|
48
|
+
moduleSpecifier: 'zod'
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// import enums
|
|
52
|
+
for (const e of model.declarations.filter(d => (0, _ast.isEnum)(d))) {
|
|
53
|
+
sf.addImportDeclaration({
|
|
54
|
+
namedImports: [{
|
|
55
|
+
name: e.name
|
|
56
|
+
}],
|
|
57
|
+
moduleSpecifier: '@prisma/client'
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const models = model.declarations.filter(d => (0, _ast.isDataModel)(d));
|
|
61
|
+
const policyMap = {};
|
|
62
|
+
for (const model of models) {
|
|
63
|
+
policyMap[model.name] = await this.generateQueryGuardForModel(model, sf);
|
|
64
|
+
}
|
|
65
|
+
const zodGenerator = new _zodSchemaGenerator.ZodSchemaGenerator();
|
|
66
|
+
sf.addVariableStatement({
|
|
67
|
+
declarationKind: _tsMorph.VariableDeclarationKind.Const,
|
|
68
|
+
declarations: [{
|
|
69
|
+
name: 'policy',
|
|
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, _changeCase.camelCase)(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
|
+
} else {
|
|
81
|
+
writer.write(`${op}: ${func},`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
writer.write(',');
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
writer.writeLine(',');
|
|
89
|
+
writer.write('schema:');
|
|
90
|
+
zodGenerator.generate(writer, models);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}]
|
|
94
|
+
});
|
|
95
|
+
sf.addStatements('export default policy');
|
|
96
|
+
sf.formatText();
|
|
97
|
+
await project.save();
|
|
98
|
+
await project.emit();
|
|
99
|
+
}
|
|
100
|
+
getPolicyExpressions(model, kind, operation) {
|
|
101
|
+
const attrs = model.attributes.filter(attr => {
|
|
102
|
+
var _attr$decl$ref;
|
|
103
|
+
return ((_attr$decl$ref = attr.decl.ref) === null || _attr$decl$ref === void 0 ? void 0 : _attr$decl$ref.name) === `@@${kind}`;
|
|
104
|
+
});
|
|
105
|
+
const checkOperation = operation === 'postUpdate' ? 'update' : operation;
|
|
106
|
+
let result = attrs.filter(attr => {
|
|
107
|
+
const opsValue = (0, _sdk.getLiteral)(attr.args[0].value);
|
|
108
|
+
if (!opsValue) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
const ops = opsValue.split(',').map(s => s.trim());
|
|
112
|
+
return ops.includes(checkOperation) || ops.includes('all');
|
|
113
|
+
}).map(attr => attr.args[1].value);
|
|
114
|
+
if (operation === 'update') {
|
|
115
|
+
result = this.processUpdatePolicies(result, false);
|
|
116
|
+
} else if (operation === 'postUpdate') {
|
|
117
|
+
result = this.processUpdatePolicies(result, true);
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
processUpdatePolicies(expressions, postUpdate) {
|
|
122
|
+
return expressions.map(expr => this.visitPolicyExpression(expr, postUpdate)).filter(e => !!e);
|
|
123
|
+
}
|
|
124
|
+
visitPolicyExpression(expr, postUpdate) {
|
|
125
|
+
if ((0, _ast.isBinaryExpr)(expr) && (expr.operator === '&&' || expr.operator === '||')) {
|
|
126
|
+
const left = this.visitPolicyExpression(expr.left, postUpdate);
|
|
127
|
+
const right = this.visitPolicyExpression(expr.right, postUpdate);
|
|
128
|
+
if (!left) return right;
|
|
129
|
+
if (!right) return left;
|
|
130
|
+
return {
|
|
131
|
+
...expr,
|
|
132
|
+
left,
|
|
133
|
+
right
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if ((0, _ast.isUnaryExpr)(expr) && expr.operator === '!') {
|
|
137
|
+
const operand = this.visitPolicyExpression(expr.operand, postUpdate);
|
|
138
|
+
if (!operand) return undefined;
|
|
139
|
+
return {
|
|
140
|
+
...expr,
|
|
141
|
+
operand
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
if (postUpdate && !this.hasFutureReference(expr)) {
|
|
145
|
+
return undefined;
|
|
146
|
+
} else if (!postUpdate && this.hasFutureReference(expr)) {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
return expr;
|
|
150
|
+
}
|
|
151
|
+
hasFutureReference(expr) {
|
|
152
|
+
for (const node of (0, _langium.streamAllContents)(expr)) {
|
|
153
|
+
var _node$function$ref;
|
|
154
|
+
if ((0, _ast.isInvocationExpr)(node) && ((_node$function$ref = node.function.ref) === null || _node$function$ref === void 0 ? void 0 : _node$function$ref.name) === 'future' && (0, _utils.isFromStdlib)(node.function.ref)) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
async generateQueryGuardForModel(model, sourceFile) {
|
|
161
|
+
const result = {};
|
|
162
|
+
|
|
163
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
164
|
+
const policies = (0, _astUtils.analyzePolicies)(model);
|
|
165
|
+
for (const kind of _pluginUtils.ALL_OPERATION_KINDS) {
|
|
166
|
+
if (policies[kind] === true || policies[kind] === false) {
|
|
167
|
+
result[kind] = policies[kind];
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const denies = this.getPolicyExpressions(model, 'deny', kind);
|
|
171
|
+
const allows = this.getPolicyExpressions(model, 'allow', kind);
|
|
172
|
+
if (kind === 'update' && allows.length === 0) {
|
|
173
|
+
// no allow rule for 'update', policy is constant based on if there's
|
|
174
|
+
// post-update counterpart
|
|
175
|
+
if (this.getPolicyExpressions(model, 'allow', 'postUpdate').length === 0) {
|
|
176
|
+
result[kind] = false;
|
|
177
|
+
continue;
|
|
178
|
+
} else {
|
|
179
|
+
result[kind] = true;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (kind === 'postUpdate' && allows.length === 0 && denies.length === 0) {
|
|
184
|
+
// no rule 'postUpdate', always allow
|
|
185
|
+
result[kind] = true;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const func = this.generateQueryGuardFunction(sourceFile, model, kind, allows, denies);
|
|
189
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
190
|
+
result[kind] = func.getName();
|
|
191
|
+
if (kind === 'postUpdate') {
|
|
192
|
+
const preValueSelect = this.generatePreValueSelect(model, allows, denies);
|
|
193
|
+
if (preValueSelect) {
|
|
194
|
+
result['preValueSelect'] = preValueSelect;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// generates an object that can be used as the 'select' argument when fetching pre-update
|
|
202
|
+
// entity value
|
|
203
|
+
generatePreValueSelect(model, allows, denies) {
|
|
204
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
205
|
+
const result = {};
|
|
206
|
+
const addPath = path => {
|
|
207
|
+
let curr = result;
|
|
208
|
+
path.forEach((seg, i) => {
|
|
209
|
+
if (i === path.length - 1) {
|
|
210
|
+
curr[seg] = true;
|
|
211
|
+
} else {
|
|
212
|
+
if (!curr[seg]) {
|
|
213
|
+
curr[seg] = {
|
|
214
|
+
select: {}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
curr = curr[seg].select;
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
};
|
|
221
|
+
const visit = node => {
|
|
222
|
+
if ((0, _ast.isReferenceExpr)(node)) {
|
|
223
|
+
const target = (0, _sdk.resolved)(node.target);
|
|
224
|
+
if ((0, _ast.isDataModelField)(target)) {
|
|
225
|
+
// a field selection, it's a terminal
|
|
226
|
+
return [target.name];
|
|
227
|
+
}
|
|
228
|
+
} else if ((0, _ast.isMemberAccessExpr)(node)) {
|
|
229
|
+
if ((0, _utils2.isFutureExpr)(node.operand)) {
|
|
230
|
+
// future().field is not subject to pre-update select
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// build a selection path inside-out for chained member access
|
|
235
|
+
const inner = visit(node.operand);
|
|
236
|
+
if (inner) {
|
|
237
|
+
return [...inner, node.member.$refText];
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return undefined;
|
|
241
|
+
};
|
|
242
|
+
for (const rule of [...allows, ...denies]) {
|
|
243
|
+
for (const expr of (0, _langium.streamAllContents)(rule).filter(node => (0, _ast.isExpression)(node))) {
|
|
244
|
+
// only care about member access and reference expressions
|
|
245
|
+
if (!(0, _ast.isMemberAccessExpr)(expr) && !(0, _ast.isReferenceExpr)(expr)) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (expr.$container.$type === _ast.MemberAccessExpr) {
|
|
249
|
+
// only visit top-level member access
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const path = visit(expr);
|
|
253
|
+
if (path) {
|
|
254
|
+
var _expr$$resolvedType;
|
|
255
|
+
if ((0, _ast.isDataModel)((_expr$$resolvedType = expr.$resolvedType) === null || _expr$$resolvedType === void 0 ? void 0 : _expr$$resolvedType.decl)) {
|
|
256
|
+
// member selection ended at a data model field, include its 'id'
|
|
257
|
+
path.push('id');
|
|
258
|
+
}
|
|
259
|
+
addPath(path);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return Object.keys(result).length === 0 ? null : result;
|
|
264
|
+
}
|
|
265
|
+
generateQueryGuardFunction(sourceFile, model, kind, allows, denies) {
|
|
266
|
+
const func = sourceFile.addFunction({
|
|
267
|
+
name: model.name + '_' + kind,
|
|
268
|
+
returnType: 'any',
|
|
269
|
+
parameters: [{
|
|
270
|
+
name: 'context',
|
|
271
|
+
type: 'QueryContext'
|
|
272
|
+
}]
|
|
273
|
+
}).addBody();
|
|
274
|
+
|
|
275
|
+
// check if any allow or deny rule contains 'auth()' invocation
|
|
276
|
+
let hasAuthRef = false;
|
|
277
|
+
for (const node of [...denies, ...allows]) {
|
|
278
|
+
for (const child of (0, _langium.streamAllContents)(node)) {
|
|
279
|
+
if ((0, _ast.isInvocationExpr)(child) && (0, _sdk.resolved)(child.function).name === 'auth') {
|
|
280
|
+
hasAuthRef = true;
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (hasAuthRef) {
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (hasAuthRef) {
|
|
289
|
+
const userModel = model.$container.declarations.find(decl => (0, _ast.isDataModel)(decl) && decl.name === 'User');
|
|
290
|
+
if (!userModel) {
|
|
291
|
+
throw new _sdk.PluginError('User model not found');
|
|
292
|
+
}
|
|
293
|
+
const userIdField = (0, _astUtils.getIdField)(userModel);
|
|
294
|
+
if (!userIdField) {
|
|
295
|
+
throw new _sdk.PluginError('User model does not have an id field');
|
|
296
|
+
}
|
|
297
|
+
func.addStatements(
|
|
298
|
+
// make sure user id is always available
|
|
299
|
+
`const user = context.user?.${userIdField.name} ? context.user : { ...context.user, ${userIdField.name}: '${UNKNOWN_USER_ID}' };`);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// r = <guard object>;
|
|
303
|
+
func.addStatements(writer => {
|
|
304
|
+
writer.write('return ');
|
|
305
|
+
const exprWriter = new _expressionWriter.ExpressionWriter(writer, kind === 'postUpdate');
|
|
306
|
+
const writeDenies = () => {
|
|
307
|
+
writer.conditionalWrite(denies.length > 1, '{ AND: [');
|
|
308
|
+
denies.forEach((expr, i) => {
|
|
309
|
+
writer.inlineBlock(() => {
|
|
310
|
+
writer.write('NOT: ');
|
|
311
|
+
exprWriter.write(expr);
|
|
312
|
+
});
|
|
313
|
+
writer.conditionalWrite(i !== denies.length - 1, ',');
|
|
314
|
+
});
|
|
315
|
+
writer.conditionalWrite(denies.length > 1, ']}');
|
|
316
|
+
};
|
|
317
|
+
const writeAllows = () => {
|
|
318
|
+
writer.conditionalWrite(allows.length > 1, '{ OR: [');
|
|
319
|
+
allows.forEach((expr, i) => {
|
|
320
|
+
exprWriter.write(expr);
|
|
321
|
+
writer.conditionalWrite(i !== allows.length - 1, ',');
|
|
322
|
+
});
|
|
323
|
+
writer.conditionalWrite(allows.length > 1, ']}');
|
|
324
|
+
};
|
|
325
|
+
if (allows.length > 0 && denies.length > 0) {
|
|
326
|
+
writer.write('{ AND: [');
|
|
327
|
+
writeDenies();
|
|
328
|
+
writer.write(',');
|
|
329
|
+
writeAllows();
|
|
330
|
+
writer.write(']}');
|
|
331
|
+
} else if (denies.length > 0) {
|
|
332
|
+
writeDenies();
|
|
333
|
+
} else if (allows.length > 0) {
|
|
334
|
+
writeAllows();
|
|
335
|
+
} else {
|
|
336
|
+
// disallow any operation
|
|
337
|
+
writer.write(`{ ${_sdk.GUARD_FIELD_NAME}: false }`);
|
|
338
|
+
}
|
|
339
|
+
writer.write(';');
|
|
340
|
+
});
|
|
341
|
+
return func;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
exports.default = PolicyGenerator;
|
|
345
|
+
//# sourceMappingURL=policy-guard-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-guard-generator.js","names":["UNKNOWN_USER_ID","PolicyGenerator","generate","model","options","output","getDefaultOutputFolder","console","error","name","project","Project","sf","createSourceFile","path","join","undefined","overwrite","addImportDeclaration","namedImports","moduleSpecifier","RUNTIME_PACKAGE","isTypeOnly","e","declarations","filter","d","isEnum","models","isDataModel","policyMap","generateQueryGuardForModel","zodGenerator","ZodSchemaGenerator","addVariableStatement","declarationKind","VariableDeclarationKind","Const","initializer","writer","block","write","inlineBlock","map","Object","entries","camelCase","op","func","JSON","stringify","writeLine","addStatements","formatText","save","emit","getPolicyExpressions","kind","operation","attrs","attributes","attr","decl","ref","checkOperation","result","opsValue","getLiteral","args","value","ops","split","s","trim","includes","processUpdatePolicies","expressions","postUpdate","expr","visitPolicyExpression","isBinaryExpr","operator","left","right","isUnaryExpr","operand","hasFutureReference","node","streamAllContents","isInvocationExpr","function","isFromStdlib","sourceFile","policies","analyzePolicies","ALL_OPERATION_KINDS","denies","allows","length","generateQueryGuardFunction","getName","preValueSelect","generatePreValueSelect","addPath","curr","forEach","seg","i","select","visit","isReferenceExpr","target","resolved","isDataModelField","isMemberAccessExpr","isFutureExpr","inner","member","$refText","rule","isExpression","$container","$type","MemberAccessExpr","$resolvedType","push","keys","addFunction","returnType","parameters","type","addBody","hasAuthRef","child","userModel","find","PluginError","userIdField","getIdField","exprWriter","ExpressionWriter","writeDenies","conditionalWrite","writeAllows","GUARD_FIELD_NAME"],"sources":["../../../src/plugins/access-policy/policy-guard-generator.ts"],"sourcesContent":["import {\n DataModel,\n Expression,\n MemberAccessExpr,\n Model,\n isBinaryExpr,\n isDataModel,\n isDataModelField,\n isEnum,\n isExpression,\n isInvocationExpr,\n isMemberAccessExpr,\n isReferenceExpr,\n isUnaryExpr,\n} from '@zenstackhq/language/ast';\nimport { PolicyKind, PolicyOperationKind } from '@zenstackhq/runtime';\nimport { GUARD_FIELD_NAME, PluginError, PluginOptions, getLiteral, resolved } from '@zenstackhq/sdk';\nimport { camelCase } from 'change-case';\nimport { streamAllContents } from 'langium';\nimport path from 'path';\nimport { FunctionDeclaration, Project, SourceFile, VariableDeclarationKind } from 'ts-morph';\nimport { name } from '.';\nimport { isFromStdlib } from '../../language-server/utils';\nimport { analyzePolicies, getIdField } from '../../utils/ast-utils';\nimport { ALL_OPERATION_KINDS, RUNTIME_PACKAGE, getDefaultOutputFolder } from '../plugin-utils';\nimport { ExpressionWriter } from './expression-writer';\nimport { isFutureExpr } from './utils';\nimport { ZodSchemaGenerator } from './zod-schema-generator';\n\nconst UNKNOWN_USER_ID = 'zenstack_unknown_user';\n\n/**\n * Generates source file that contains Prisma query guard objects used for injecting database queries\n */\nexport default class PolicyGenerator {\n async generate(model: Model, options: PluginOptions) {\n const output = options.output ? (options.output as string) : getDefaultOutputFolder();\n if (!output) {\n console.error(`Unable to determine output path, not running plugin ${name}`);\n return;\n }\n\n const project = new Project();\n const sf = project.createSourceFile(path.join(output, 'policy.ts'), undefined, { overwrite: true });\n\n sf.addImportDeclaration({\n namedImports: [{ name: 'QueryContext' }],\n moduleSpecifier: `${RUNTIME_PACKAGE}`,\n isTypeOnly: true,\n });\n\n sf.addImportDeclaration({\n namedImports: [{ name: 'z' }],\n moduleSpecifier: 'zod',\n });\n\n // import enums\n for (const e of model.declarations.filter((d) => isEnum(d))) {\n sf.addImportDeclaration({\n namedImports: [{ name: e.name }],\n moduleSpecifier: '@prisma/client',\n });\n }\n\n const models = model.declarations.filter((d) => isDataModel(d)) as DataModel[];\n\n const policyMap: Record<string, Record<string, string | boolean | object>> = {};\n for (const model of models) {\n policyMap[model.name] = await this.generateQueryGuardForModel(model, sf);\n }\n\n const zodGenerator = new ZodSchemaGenerator();\n\n sf.addVariableStatement({\n declarationKind: VariableDeclarationKind.Const,\n declarations: [\n {\n name: 'policy',\n initializer: (writer) => {\n writer.block(() => {\n writer.write('guard:');\n writer.inlineBlock(() => {\n for (const [model, map] of Object.entries(policyMap)) {\n writer.write(`${camelCase(model)}:`);\n writer.inlineBlock(() => {\n for (const [op, func] of Object.entries(map)) {\n if (typeof func === 'object') {\n writer.write(`${op}: ${JSON.stringify(func)},`);\n } else {\n writer.write(`${op}: ${func},`);\n }\n }\n });\n writer.write(',');\n }\n });\n\n writer.writeLine(',');\n\n writer.write('schema:');\n zodGenerator.generate(writer, models);\n });\n },\n },\n ],\n });\n\n sf.addStatements('export default policy');\n\n sf.formatText();\n await project.save();\n await project.emit();\n }\n\n private getPolicyExpressions(model: DataModel, kind: PolicyKind, operation: PolicyOperationKind) {\n const attrs = model.attributes.filter((attr) => attr.decl.ref?.name === `@@${kind}`);\n\n const checkOperation = operation === 'postUpdate' ? 'update' : operation;\n\n let result = attrs\n .filter((attr) => {\n const opsValue = getLiteral<string>(attr.args[0].value);\n if (!opsValue) {\n return false;\n }\n const ops = opsValue.split(',').map((s) => s.trim());\n return ops.includes(checkOperation) || ops.includes('all');\n })\n .map((attr) => attr.args[1].value);\n\n if (operation === 'update') {\n result = this.processUpdatePolicies(result, false);\n } else if (operation === 'postUpdate') {\n result = this.processUpdatePolicies(result, true);\n }\n\n return result;\n }\n\n private processUpdatePolicies(expressions: Expression[], postUpdate: boolean) {\n return expressions\n .map((expr) => this.visitPolicyExpression(expr, postUpdate))\n .filter((e): e is Expression => !!e);\n }\n\n private visitPolicyExpression(expr: Expression, postUpdate: boolean): Expression | undefined {\n if (isBinaryExpr(expr) && (expr.operator === '&&' || expr.operator === '||')) {\n const left = this.visitPolicyExpression(expr.left, postUpdate);\n const right = this.visitPolicyExpression(expr.right, postUpdate);\n if (!left) return right;\n if (!right) return left;\n return { ...expr, left, right };\n }\n\n if (isUnaryExpr(expr) && expr.operator === '!') {\n const operand = this.visitPolicyExpression(expr.operand, postUpdate);\n if (!operand) return undefined;\n return { ...expr, operand };\n }\n\n if (postUpdate && !this.hasFutureReference(expr)) {\n return undefined;\n } else if (!postUpdate && this.hasFutureReference(expr)) {\n return undefined;\n }\n\n return expr;\n }\n\n private hasFutureReference(expr: Expression) {\n for (const node of streamAllContents(expr)) {\n if (isInvocationExpr(node) && node.function.ref?.name === 'future' && isFromStdlib(node.function.ref)) {\n return true;\n }\n }\n return false;\n }\n\n private async generateQueryGuardForModel(model: DataModel, sourceFile: SourceFile) {\n const result: Record<string, string | boolean | object> = {};\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const policies: any = analyzePolicies(model);\n\n for (const kind of ALL_OPERATION_KINDS) {\n if (policies[kind] === true || policies[kind] === false) {\n result[kind] = policies[kind];\n continue;\n }\n\n const denies = this.getPolicyExpressions(model, 'deny', kind);\n const allows = this.getPolicyExpressions(model, 'allow', kind);\n\n if (kind === 'update' && allows.length === 0) {\n // no allow rule for 'update', policy is constant based on if there's\n // post-update counterpart\n if (this.getPolicyExpressions(model, 'allow', 'postUpdate').length === 0) {\n result[kind] = false;\n continue;\n } else {\n result[kind] = true;\n continue;\n }\n }\n\n if (kind === 'postUpdate' && allows.length === 0 && denies.length === 0) {\n // no rule 'postUpdate', always allow\n result[kind] = true;\n continue;\n }\n\n const func = this.generateQueryGuardFunction(sourceFile, model, kind, allows, denies);\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n result[kind] = func.getName()!;\n\n if (kind === 'postUpdate') {\n const preValueSelect = this.generatePreValueSelect(model, allows, denies);\n if (preValueSelect) {\n result['preValueSelect'] = preValueSelect;\n }\n }\n }\n return result;\n }\n\n // generates an object that can be used as the 'select' argument when fetching pre-update\n // entity value\n private generatePreValueSelect(model: DataModel, allows: Expression[], denies: Expression[]): object {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result: any = {};\n const addPath = (path: string[]) => {\n let curr = result;\n path.forEach((seg, i) => {\n if (i === path.length - 1) {\n curr[seg] = true;\n } else {\n if (!curr[seg]) {\n curr[seg] = { select: {} };\n }\n curr = curr[seg].select;\n }\n });\n };\n\n const visit = (node: Expression): string[] | undefined => {\n if (isReferenceExpr(node)) {\n const target = resolved(node.target);\n if (isDataModelField(target)) {\n // a field selection, it's a terminal\n return [target.name];\n }\n } else if (isMemberAccessExpr(node)) {\n if (isFutureExpr(node.operand)) {\n // future().field is not subject to pre-update select\n return undefined;\n }\n\n // build a selection path inside-out for chained member access\n const inner = visit(node.operand);\n if (inner) {\n return [...inner, node.member.$refText];\n }\n }\n return undefined;\n };\n\n for (const rule of [...allows, ...denies]) {\n for (const expr of streamAllContents(rule).filter((node): node is Expression => isExpression(node))) {\n // only care about member access and reference expressions\n if (!isMemberAccessExpr(expr) && !isReferenceExpr(expr)) {\n continue;\n }\n\n if (expr.$container.$type === MemberAccessExpr) {\n // only visit top-level member access\n continue;\n }\n\n const path = visit(expr);\n if (path) {\n if (isDataModel(expr.$resolvedType?.decl)) {\n // member selection ended at a data model field, include its 'id'\n path.push('id');\n }\n addPath(path);\n }\n }\n }\n\n return Object.keys(result).length === 0 ? null : result;\n }\n\n private generateQueryGuardFunction(\n sourceFile: SourceFile,\n model: DataModel,\n kind: PolicyOperationKind,\n allows: Expression[],\n denies: Expression[]\n ): FunctionDeclaration {\n const func = sourceFile\n .addFunction({\n name: model.name + '_' + kind,\n returnType: 'any',\n parameters: [\n {\n name: 'context',\n type: 'QueryContext',\n },\n ],\n })\n .addBody();\n\n // check if any allow or deny rule contains 'auth()' invocation\n let hasAuthRef = false;\n for (const node of [...denies, ...allows]) {\n for (const child of streamAllContents(node)) {\n if (isInvocationExpr(child) && resolved(child.function).name === 'auth') {\n hasAuthRef = true;\n break;\n }\n }\n if (hasAuthRef) {\n break;\n }\n }\n\n if (hasAuthRef) {\n const userModel = model.$container.declarations.find(\n (decl): decl is DataModel => isDataModel(decl) && decl.name === 'User'\n );\n if (!userModel) {\n throw new PluginError('User model not found');\n }\n const userIdField = getIdField(userModel);\n if (!userIdField) {\n throw new PluginError('User model does not have an id field');\n }\n func.addStatements(\n // make sure user id is always available\n `const user = context.user?.${userIdField.name} ? context.user : { ...context.user, ${userIdField.name}: '${UNKNOWN_USER_ID}' };`\n );\n }\n\n // r = <guard object>;\n func.addStatements((writer) => {\n writer.write('return ');\n const exprWriter = new ExpressionWriter(writer, kind === 'postUpdate');\n const writeDenies = () => {\n writer.conditionalWrite(denies.length > 1, '{ AND: [');\n denies.forEach((expr, i) => {\n writer.inlineBlock(() => {\n writer.write('NOT: ');\n exprWriter.write(expr);\n });\n writer.conditionalWrite(i !== denies.length - 1, ',');\n });\n writer.conditionalWrite(denies.length > 1, ']}');\n };\n\n const writeAllows = () => {\n writer.conditionalWrite(allows.length > 1, '{ OR: [');\n allows.forEach((expr, i) => {\n exprWriter.write(expr);\n writer.conditionalWrite(i !== allows.length - 1, ',');\n });\n writer.conditionalWrite(allows.length > 1, ']}');\n };\n\n if (allows.length > 0 && denies.length > 0) {\n writer.write('{ AND: [');\n writeDenies();\n writer.write(',');\n writeAllows();\n writer.write(']}');\n } else if (denies.length > 0) {\n writeDenies();\n } else if (allows.length > 0) {\n writeAllows();\n } else {\n // disallow any operation\n writer.write(`{ ${GUARD_FIELD_NAME}: false }`);\n }\n writer.write(';');\n });\n return func;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Expression } from '@zenstackhq/language/ast';
|
|
2
|
+
/**
|
|
3
|
+
* Transforms ZModel expression to plain TypeScript expression.
|
|
4
|
+
*/
|
|
5
|
+
export default class TypeScriptExpressionTransformer {
|
|
6
|
+
private readonly isPostGuard;
|
|
7
|
+
/**
|
|
8
|
+
* Constructs a new TypeScriptExpressionTransformer.
|
|
9
|
+
*
|
|
10
|
+
* @param isPostGuard indicates if we're writing for post-update conditions
|
|
11
|
+
*/
|
|
12
|
+
constructor(isPostGuard?: boolean);
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* @param expr
|
|
16
|
+
* @returns
|
|
17
|
+
*/
|
|
18
|
+
transform(expr: Expression): string;
|
|
19
|
+
private this;
|
|
20
|
+
private memberAccess;
|
|
21
|
+
private invocation;
|
|
22
|
+
private reference;
|
|
23
|
+
private null;
|
|
24
|
+
private array;
|
|
25
|
+
private literal;
|
|
26
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _ast = require("@zenstackhq/language/ast");
|
|
8
|
+
var _sdk = require("@zenstackhq/sdk");
|
|
9
|
+
var _utils = require("./utils");
|
|
10
|
+
/**
|
|
11
|
+
* Transforms ZModel expression to plain TypeScript expression.
|
|
12
|
+
*/
|
|
13
|
+
class TypeScriptExpressionTransformer {
|
|
14
|
+
/**
|
|
15
|
+
* Constructs a new TypeScriptExpressionTransformer.
|
|
16
|
+
*
|
|
17
|
+
* @param isPostGuard indicates if we're writing for post-update conditions
|
|
18
|
+
*/
|
|
19
|
+
constructor(isPostGuard = false) {
|
|
20
|
+
this.isPostGuard = isPostGuard;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
*
|
|
25
|
+
* @param expr
|
|
26
|
+
* @returns
|
|
27
|
+
*/
|
|
28
|
+
transform(expr) {
|
|
29
|
+
switch (expr.$type) {
|
|
30
|
+
case _ast.LiteralExpr:
|
|
31
|
+
return this.literal(expr);
|
|
32
|
+
case _ast.ArrayExpr:
|
|
33
|
+
return this.array(expr);
|
|
34
|
+
case _ast.NullExpr:
|
|
35
|
+
return this.null();
|
|
36
|
+
case _ast.ThisExpr:
|
|
37
|
+
return this.this(expr);
|
|
38
|
+
case _ast.ReferenceExpr:
|
|
39
|
+
return this.reference(expr);
|
|
40
|
+
case _ast.InvocationExpr:
|
|
41
|
+
return this.invocation(expr);
|
|
42
|
+
case _ast.MemberAccessExpr:
|
|
43
|
+
return this.memberAccess(expr);
|
|
44
|
+
default:
|
|
45
|
+
throw new _sdk.PluginError(`Unsupported expression type: ${expr.$type}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
50
|
+
this(expr) {
|
|
51
|
+
// "this" is mapped to id comparison
|
|
52
|
+
return 'id';
|
|
53
|
+
}
|
|
54
|
+
memberAccess(expr) {
|
|
55
|
+
if (!expr.member.ref) {
|
|
56
|
+
throw new _sdk.PluginError(`Unresolved MemberAccessExpr`);
|
|
57
|
+
}
|
|
58
|
+
if ((0, _ast.isThisExpr)(expr.operand)) {
|
|
59
|
+
return expr.member.ref.name;
|
|
60
|
+
} else if ((0, _utils.isFutureExpr)(expr.operand)) {
|
|
61
|
+
if (!this.isPostGuard) {
|
|
62
|
+
throw new _sdk.PluginError(`future() is only supported in postUpdate rules`);
|
|
63
|
+
}
|
|
64
|
+
return expr.member.ref.name;
|
|
65
|
+
} else {
|
|
66
|
+
return `${this.transform(expr.operand)}?.${expr.member.ref.name}`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
invocation(expr) {
|
|
70
|
+
var _expr$function$ref;
|
|
71
|
+
if (((_expr$function$ref = expr.function.ref) === null || _expr$function$ref === void 0 ? void 0 : _expr$function$ref.name) === 'auth') {
|
|
72
|
+
return 'user';
|
|
73
|
+
} else {
|
|
74
|
+
var _expr$function$ref2;
|
|
75
|
+
throw new _sdk.PluginError(`Function invocation is not supported: ${(_expr$function$ref2 = expr.function.ref) === null || _expr$function$ref2 === void 0 ? void 0 : _expr$function$ref2.name}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
reference(expr) {
|
|
79
|
+
if (!expr.target.ref) {
|
|
80
|
+
throw new _sdk.PluginError(`Unresolved ReferenceExpr`);
|
|
81
|
+
}
|
|
82
|
+
if ((0, _ast.isEnumField)(expr.target.ref)) {
|
|
83
|
+
return `${expr.target.ref.$container.name}.${expr.target.ref.name}`;
|
|
84
|
+
} else {
|
|
85
|
+
if (this.isPostGuard) {
|
|
86
|
+
// if we're processing post-update, any direct field access should be
|
|
87
|
+
// treated as access to context.preValue, which is entity's value before
|
|
88
|
+
// the update
|
|
89
|
+
return `context.preValue?.${expr.target.ref.name}`;
|
|
90
|
+
} else {
|
|
91
|
+
return expr.target.ref.name;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
null() {
|
|
96
|
+
return 'null';
|
|
97
|
+
}
|
|
98
|
+
array(expr) {
|
|
99
|
+
return `[${expr.items.map(item => this.transform(item)).join(', ')}]`;
|
|
100
|
+
}
|
|
101
|
+
literal(expr) {
|
|
102
|
+
if (typeof expr.value === 'string') {
|
|
103
|
+
return `'${expr.value}'`;
|
|
104
|
+
} else {
|
|
105
|
+
return expr.value.toString();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.default = TypeScriptExpressionTransformer;
|
|
110
|
+
//# sourceMappingURL=typescript-expression-transformer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typescript-expression-transformer.js","names":["TypeScriptExpressionTransformer","constructor","isPostGuard","transform","expr","$type","LiteralExpr","literal","ArrayExpr","array","NullExpr","null","ThisExpr","this","ReferenceExpr","reference","InvocationExpr","invocation","MemberAccessExpr","memberAccess","PluginError","member","ref","isThisExpr","operand","name","isFutureExpr","function","target","isEnumField","$container","items","map","item","join","value","toString"],"sources":["../../../src/plugins/access-policy/typescript-expression-transformer.ts"],"sourcesContent":["import {\n ArrayExpr,\n Expression,\n InvocationExpr,\n LiteralExpr,\n MemberAccessExpr,\n NullExpr,\n ReferenceExpr,\n ThisExpr,\n isEnumField,\n isThisExpr,\n} from '@zenstackhq/language/ast';\nimport { PluginError } from '@zenstackhq/sdk';\nimport { isFutureExpr } from './utils';\n\n/**\n * Transforms ZModel expression to plain TypeScript expression.\n */\nexport default class TypeScriptExpressionTransformer {\n /**\n * Constructs a new TypeScriptExpressionTransformer.\n *\n * @param isPostGuard indicates if we're writing for post-update conditions\n */\n constructor(private readonly isPostGuard = false) {}\n\n /**\n *\n * @param expr\n * @returns\n */\n transform(expr: Expression): string {\n switch (expr.$type) {\n case LiteralExpr:\n return this.literal(expr as LiteralExpr);\n\n case ArrayExpr:\n return this.array(expr as ArrayExpr);\n\n case NullExpr:\n return this.null();\n\n case ThisExpr:\n return this.this(expr as ThisExpr);\n\n case ReferenceExpr:\n return this.reference(expr as ReferenceExpr);\n\n case InvocationExpr:\n return this.invocation(expr as InvocationExpr);\n\n case MemberAccessExpr:\n return this.memberAccess(expr as MemberAccessExpr);\n\n default:\n throw new PluginError(`Unsupported expression type: ${expr.$type}`);\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n private this(expr: ThisExpr) {\n // \"this\" is mapped to id comparison\n return 'id';\n }\n\n private memberAccess(expr: MemberAccessExpr) {\n if (!expr.member.ref) {\n throw new PluginError(`Unresolved MemberAccessExpr`);\n }\n\n if (isThisExpr(expr.operand)) {\n return expr.member.ref.name;\n } else if (isFutureExpr(expr.operand)) {\n if (!this.isPostGuard) {\n throw new PluginError(`future() is only supported in postUpdate rules`);\n }\n return expr.member.ref.name;\n } else {\n return `${this.transform(expr.operand)}?.${expr.member.ref.name}`;\n }\n }\n\n private invocation(expr: InvocationExpr) {\n if (expr.function.ref?.name === 'auth') {\n return 'user';\n } else {\n throw new PluginError(`Function invocation is not supported: ${expr.function.ref?.name}`);\n }\n }\n\n private reference(expr: ReferenceExpr) {\n if (!expr.target.ref) {\n throw new PluginError(`Unresolved ReferenceExpr`);\n }\n\n if (isEnumField(expr.target.ref)) {\n return `${expr.target.ref.$container.name}.${expr.target.ref.name}`;\n } else {\n if (this.isPostGuard) {\n // if we're processing post-update, any direct field access should be\n // treated as access to context.preValue, which is entity's value before\n // the update\n return `context.preValue?.${expr.target.ref.name}`;\n } else {\n return expr.target.ref.name;\n }\n }\n }\n\n private null() {\n return 'null';\n }\n\n private array(expr: ArrayExpr) {\n return `[${expr.items.map((item) => this.transform(item)).join(', ')}]`;\n }\n\n private literal(expr: LiteralExpr) {\n if (typeof expr.value === 'string') {\n return `'${expr.value}'`;\n } else {\n return expr.value.toString();\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isFutureExpr = isFutureExpr;
|
|
7
|
+
var _ast = require("@zenstackhq/language/ast");
|
|
8
|
+
var _utils = require("../../language-server/utils");
|
|
9
|
+
/**
|
|
10
|
+
* Returns if the given expression is a "future()" method call.
|
|
11
|
+
*/
|
|
12
|
+
function isFutureExpr(expr) {
|
|
13
|
+
var _expr$function$ref;
|
|
14
|
+
return !!((0, _ast.isInvocationExpr)(expr) && ((_expr$function$ref = expr.function.ref) === null || _expr$function$ref === void 0 ? void 0 : _expr$function$ref.name) === 'future' && (0, _utils.isFromStdlib)(expr.function.ref));
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","names":["isFutureExpr","expr","isInvocationExpr","function","ref","name","isFromStdlib"],"sources":["../../../src/plugins/access-policy/utils.ts"],"sourcesContent":["import { Expression, isInvocationExpr } from '@zenstackhq/language/ast';\nimport { isFromStdlib } from '../../language-server/utils';\n\n/**\n * Returns if the given expression is a \"future()\" method call.\n */\nexport function isFutureExpr(expr: Expression) {\n return !!(isInvocationExpr(expr) && expr.function.ref?.name === 'future' && isFromStdlib(expr.function.ref));\n}\n"],"mappings":";;;;;;AAAA;AACA;AAEA;AACA;AACA;AACO,SAASA,YAAY,CAACC,IAAgB,EAAE;EAAA;EAC3C,OAAO,CAAC,EAAE,IAAAC,qBAAgB,EAACD,IAAI,CAAC,IAAI,uBAAAA,IAAI,CAACE,QAAQ,CAACC,GAAG,uDAAjB,mBAAmBC,IAAI,MAAK,QAAQ,IAAI,IAAAC,mBAAY,EAACL,IAAI,CAACE,QAAQ,CAACC,GAAG,CAAC,CAAC;AAChH"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DataModel } from '@zenstackhq/language/ast';
|
|
2
|
+
import { CodeBlockWriter } from 'ts-morph';
|
|
3
|
+
/**
|
|
4
|
+
* Writes Zod schema for data models.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ZodSchemaGenerator {
|
|
7
|
+
generate(writer: CodeBlockWriter, models: DataModel[]): void;
|
|
8
|
+
private hasValidationAttributes;
|
|
9
|
+
private makeFieldValidator;
|
|
10
|
+
private makeZodSchema;
|
|
11
|
+
private getAttrLiteralArg;
|
|
12
|
+
}
|