@cerebruminc/yates 1.1.1 → 2.0.0
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/CHANGELOG.md +29 -0
- package/README.md +50 -7
- package/dist/escape.d.ts +2 -0
- package/dist/escape.js +36 -0
- package/dist/escape.js.map +1 -0
- package/dist/expressions.d.ts +5 -0
- package/dist/expressions.js +307 -0
- package/dist/expressions.js.map +1 -0
- package/dist/index.d.ts +26 -12
- package/dist/index.js +211 -168
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.0.0](https://github.com/cerebruminc/yates/compare/v1.2.0...v2.0.0) (2023-02-22)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* Use client extensions instead of middleware
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* Use client extensions instead of middleware ([f3fa1a3](https://github.com/cerebruminc/yates/commit/f3fa1a3d187d62031e2124d023ba726e7b810e39))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* Explicity disconnect expression Prisma client ([ccceaa3](https://github.com/cerebruminc/yates/commit/ccceaa3f3baab26251b00b631962ae633deea714))
|
|
18
|
+
* Strongly type context values in expressions ([ec5e266](https://github.com/cerebruminc/yates/commit/ec5e2668a0ba776ed4319ade0940cd2f8dc9cbed))
|
|
19
|
+
|
|
20
|
+
## [1.2.0](https://github.com/cerebruminc/yates/compare/v1.1.1...v1.2.0) (2023-02-20)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* Add functionality for using Prisma as a query builder ([22d16a8](https://github.com/cerebruminc/yates/commit/22d16a8d21ea785256fe770747517e0a249e56c3))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Bug Fixes
|
|
29
|
+
|
|
30
|
+
* Don't run integration tests when a PR is closed ([2b25093](https://github.com/cerebruminc/yates/commit/2b250937b0618d4ad7c7706286b74bca52603b22))
|
|
31
|
+
|
|
3
32
|
## [1.1.1](https://github.com/cerebruminc/yates/compare/v1.1.0...v1.1.1) (2023-01-30)
|
|
4
33
|
|
|
5
34
|
|
package/README.md
CHANGED
|
@@ -18,15 +18,29 @@
|
|
|
18
18
|
Yates is a module for implementing role based access control with Prisma. It is designed to be used with the [Prisma Client](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client) and [PostgreSQL](https://www.postgresql.org/).
|
|
19
19
|
It uses the [Row Level Security](https://www.postgresql.org/docs/9.5/ddl-rowsecurity.html) feature of PostgreSQL to provide a simple and secure way to implement role based access control that allows you to define complex access control rules and have them apply to all of your Prisma queries automatically.
|
|
20
20
|
|
|
21
|
+
## Prerequisites
|
|
22
|
+
|
|
23
|
+
Yates requires the `prisma` package ate version 4.9.0 or greater and the `@prisma/client` package at version 4.0.0 or greater. Additionally it makes use of the [Prisma Client extensions](https://www.prisma.io/docs/concepts/components/prisma-client/client-extensions) preview feature to generate rules and add RLS checking, so you will need to enable this feature in your Prisma schema.
|
|
24
|
+
|
|
25
|
+
````prisma
|
|
26
|
+
generator client {
|
|
27
|
+
provider = "prisma-client-js"
|
|
28
|
+
previewFeatures = ["clientExtensions"]
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
21
32
|
## Installation
|
|
22
33
|
|
|
23
34
|
```bash
|
|
24
35
|
npm i @cerebruminc/yates
|
|
25
|
-
|
|
36
|
+
````
|
|
26
37
|
|
|
27
38
|
## Usage
|
|
28
39
|
|
|
29
|
-
Once you've installed Yates, you can use it in your Prisma project by importing it and calling the `setup` function. This function takes a Prisma Client instance and a configuration object as arguments
|
|
40
|
+
Once you've installed Yates, you can use it in your Prisma project by importing it and calling the `setup` function. This function takes a Prisma Client instance and a configuration object as arguments and returns a client that can intercept all queries and apply the appropriate row level security policies to them.
|
|
41
|
+
Yates uses client extensions to generate the RLS rules and add the RLS checking to the Prisma Client queries. This means that you can use the Prisma Client as you normally would, and Yates will automatically apply the appropriate RLS policies to each query. It also means that you will need to apply your middleware _before_ creating the Yates client, as middleware cannot be applied to an extended client.
|
|
42
|
+
Client extensions share the same API as the Prisma Client, you can use the Yates client as a drop-in replacement for the Prisma Client in your application.
|
|
43
|
+
Client extensions also share the same connection pool as the base client, which means that you can freely create new Yates clients with minimal performance impact.
|
|
30
44
|
|
|
31
45
|
The `setup` function will generate CRUD abilities for each model in your Prisma schema, as well as any additional abilities that you have defined in your configuration. It will then create a new PG role for each ability and apply the appropriate row level security policies to each role. Finally, it will create a new PG role for each user role you specify and grant them the appropriate abilities.
|
|
32
46
|
For Yates to be able to set the correct user role for each request, you must pass a function called `getContext` in the `setup` configuration that will return the user role for the current request. This function will be called for each request and the user role returned will be used to set the `role` in the current session. If you want to bypass RLS completely for a specific role, you can return `null` from the `getContext` function for that role.
|
|
@@ -39,16 +53,44 @@ import { PrismaClient } from "@prisma/client";
|
|
|
39
53
|
|
|
40
54
|
const prisma = new PrismaClient();
|
|
41
55
|
|
|
42
|
-
await setup({
|
|
56
|
+
const client = await setup({
|
|
43
57
|
prisma,
|
|
44
58
|
// Define any custom abilities that you want to add to the system.
|
|
45
59
|
customAbilities: () => ({
|
|
46
60
|
USER: {
|
|
61
|
+
Post: {
|
|
62
|
+
insertOwnPost: {
|
|
63
|
+
description: "Insert own post",
|
|
64
|
+
// You can express the rule as a Prisma `where` clause.
|
|
65
|
+
expression: (client, row, context) => {
|
|
66
|
+
return {
|
|
67
|
+
// This expression uses a context setting returned by the getContext function
|
|
68
|
+
authorId: context('user.id')
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
operation: "INSERT",
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
Comment: {
|
|
75
|
+
deleteOnOwnPost: {
|
|
76
|
+
description: "Delete comment on own post",
|
|
77
|
+
// You can also express the rule as a conventional Prisma query.
|
|
78
|
+
expression: (client, row, context) => {
|
|
79
|
+
return client.post.findFirst({
|
|
80
|
+
where: {
|
|
81
|
+
id: row('postId'),
|
|
82
|
+
authorId: context('user.id')
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
},
|
|
86
|
+
operation: "DELETE",
|
|
87
|
+
},
|
|
88
|
+
}
|
|
47
89
|
User: {
|
|
48
90
|
updateOwnUser: {
|
|
49
91
|
description: "Update own user",
|
|
50
|
-
//
|
|
51
|
-
expression: `current_setting('
|
|
92
|
+
// For low-level control you can also write expressions as a raw SQL string.
|
|
93
|
+
expression: `current_setting('user.id') = "id"`,
|
|
52
94
|
operation: "UPDATE",
|
|
53
95
|
},
|
|
54
96
|
}
|
|
@@ -82,11 +124,12 @@ await setup({
|
|
|
82
124
|
return {
|
|
83
125
|
role,
|
|
84
126
|
context: {
|
|
85
|
-
// This context setting will be available in ability expressions using `current_setting('
|
|
86
|
-
'
|
|
127
|
+
// This context setting will be available in ability expressions using `current_setting('user.id')`
|
|
128
|
+
'user.id': user.id,
|
|
87
129
|
},
|
|
88
130
|
};
|
|
89
131
|
},
|
|
132
|
+
});
|
|
90
133
|
```
|
|
91
134
|
|
|
92
135
|
## Configuration
|
package/dist/escape.d.ts
ADDED
package/dist/escape.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Source borrowed from node-postgrs (which borrows from PG itself)
|
|
3
|
+
// https://github.com/brianc/node-postgres/blob/3f6760c62ee2a901d374b5e50c2f025b7d550315/packages/pg/lib/client.js#L408-L437
|
|
4
|
+
// We need to manually escape strings because we're interpolating client values into SQL statements that don't support `PREPARE`, such as `CREATE POLICY`
|
|
5
|
+
exports.__esModule = true;
|
|
6
|
+
exports.escapeLiteral = exports.escapeIdentifier = void 0;
|
|
7
|
+
// Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c
|
|
8
|
+
var escapeIdentifier = function (str) {
|
|
9
|
+
return "\"".concat(str.replace(/"/g, '""'), "\"");
|
|
10
|
+
};
|
|
11
|
+
exports.escapeIdentifier = escapeIdentifier;
|
|
12
|
+
// Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c
|
|
13
|
+
var escapeLiteral = function (str) {
|
|
14
|
+
var hasBackslash = false;
|
|
15
|
+
var escaped = "'";
|
|
16
|
+
for (var i = 0; i < str.length; i++) {
|
|
17
|
+
var c = str[i];
|
|
18
|
+
if (c === "'") {
|
|
19
|
+
escaped += c + c;
|
|
20
|
+
}
|
|
21
|
+
else if (c === "\\") {
|
|
22
|
+
escaped += c + c;
|
|
23
|
+
hasBackslash = true;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
escaped += c;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
escaped += "'";
|
|
30
|
+
if (hasBackslash === true) {
|
|
31
|
+
escaped = " E".concat(escaped);
|
|
32
|
+
}
|
|
33
|
+
return escaped;
|
|
34
|
+
};
|
|
35
|
+
exports.escapeLiteral = escapeLiteral;
|
|
36
|
+
//# sourceMappingURL=escape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escape.js","sourceRoot":"","sources":["../src/escape.ts"],"names":[],"mappings":";AAAA,mEAAmE;AACnE,4HAA4H;AAC5H,yJAAyJ;;;AAEzJ,6EAA6E;AACtE,IAAM,gBAAgB,GAAG,UAAU,GAAW;IACpD,OAAO,YAAI,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,OAAG,CAAC;AACvC,CAAC,CAAC;AAFW,QAAA,gBAAgB,oBAE3B;AAEF,6EAA6E;AACtE,IAAM,aAAa,GAAG,UAAU,GAAW;IACjD,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,IAAI,OAAO,GAAG,GAAG,CAAC;IAElB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACpC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,EAAE;YACd,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;SACjB;aAAM,IAAI,CAAC,KAAK,IAAI,EAAE;YACtB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;YACjB,YAAY,GAAG,IAAI,CAAC;SACpB;aAAM;YACN,OAAO,IAAI,CAAC,CAAC;SACb;KACD;IAED,OAAO,IAAI,GAAG,CAAC;IAEf,IAAI,YAAY,KAAK,IAAI,EAAE;QAC1B,OAAO,GAAG,YAAK,OAAO,CAAE,CAAC;KACzB;IAED,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC;AAvBW,QAAA,aAAa,iBAuBxB"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { PrismaClient } from "@prisma/client";
|
|
2
|
+
export type Expression<ContextKeys extends string = string> = string | ((client: PrismaClient, row: (col: string) => any, context: (key: ContextKeys) => string) => Promise<any> | {
|
|
3
|
+
[col: string]: any;
|
|
4
|
+
});
|
|
5
|
+
export declare const expressionToSQL: (getExpression: Expression, table: string) => Promise<string>;
|
|
@@ -0,0 +1,307 @@
|
|
|
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 __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
|
+
};
|
|
41
|
+
exports.__esModule = true;
|
|
42
|
+
exports.expressionToSQL = void 0;
|
|
43
|
+
var client_1 = require("@prisma/client");
|
|
44
|
+
var random_1 = __importDefault(require("lodash/random"));
|
|
45
|
+
var matches_1 = __importDefault(require("lodash/matches"));
|
|
46
|
+
var node_sql_parser_1 = require("@lucianbuzzo/node-sql-parser");
|
|
47
|
+
var escape_1 = require("./escape");
|
|
48
|
+
var PRISMA_NUMERIC_TYPES = ["Int", "BigInt", "Float", "Decimal"];
|
|
49
|
+
var deepFind = function (obj, subObj) {
|
|
50
|
+
var matcher = (0, matches_1["default"])(subObj);
|
|
51
|
+
for (var key in obj) {
|
|
52
|
+
if (matcher(obj[key])) {
|
|
53
|
+
return obj[key];
|
|
54
|
+
}
|
|
55
|
+
else if (typeof obj[key] === "object") {
|
|
56
|
+
var result = deepFind(obj[key], subObj);
|
|
57
|
+
if (result) {
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
var expressionRowName = function (col) { return "___yates_row_".concat(col); };
|
|
64
|
+
var expressionContext = function (context) { return "___yates_context_".concat(context); };
|
|
65
|
+
// Generate a big 32bit signed integer to use as an ID
|
|
66
|
+
var getLargeRandomInt = function () { return (0, random_1["default"])(1000000000, 2147483647); };
|
|
67
|
+
var getDmmfMetaData = function (client, model, field) {
|
|
68
|
+
var modelData = client._baseDmmf.datamodel.models.find(function (m) { return m.name === model; });
|
|
69
|
+
if (!modelData) {
|
|
70
|
+
throw new Error("Could not retrieve model data from Prisma Client for model '".concat(model, "'"));
|
|
71
|
+
}
|
|
72
|
+
var fieldData = modelData.fields.find(function (f) { return f.name === field; });
|
|
73
|
+
return fieldData;
|
|
74
|
+
};
|
|
75
|
+
// Perform substitution of Ints so that Prisma doesn't throw an error due to mismatched type values
|
|
76
|
+
// After we've captured the SQL, we can replace the Ints with the original values
|
|
77
|
+
var tokenizeWhereExpression = function (
|
|
78
|
+
/** The Prisma client to use for metadata */
|
|
79
|
+
client,
|
|
80
|
+
/** The Prisma where expression to be tokenized */
|
|
81
|
+
where,
|
|
82
|
+
/** The base table we are generating an expression for */
|
|
83
|
+
table,
|
|
84
|
+
/** The model name being queried. e.g. 'User' */
|
|
85
|
+
model,
|
|
86
|
+
/** The tokens object to add the new tokens to */
|
|
87
|
+
tokens) {
|
|
88
|
+
if (tokens === void 0) { tokens = {}; }
|
|
89
|
+
for (var field in where) {
|
|
90
|
+
// Get field data from the prisma client for the model and field being queried
|
|
91
|
+
var fieldData = getDmmfMetaData(client, model, field);
|
|
92
|
+
var int = void 0;
|
|
93
|
+
// Small loop to make sure we get a unique int for the token key
|
|
94
|
+
do {
|
|
95
|
+
int = getLargeRandomInt();
|
|
96
|
+
} while (tokens[int]);
|
|
97
|
+
var astFragment = {};
|
|
98
|
+
var value = where[field];
|
|
99
|
+
var isNumeric = PRISMA_NUMERIC_TYPES.includes(fieldData.type);
|
|
100
|
+
var isColumnName = typeof value === "string" && !!value.match(/^___yates_row_/);
|
|
101
|
+
var isContext = typeof value === "string" && !!value.match(/^___yates_context_/);
|
|
102
|
+
switch (true) {
|
|
103
|
+
case isColumnName:
|
|
104
|
+
// Substiture the yates row placeholder for the actual column name
|
|
105
|
+
var column = value.replace(/^___yates_row_/, "");
|
|
106
|
+
if (!getDmmfMetaData(client, table, column)) {
|
|
107
|
+
throw new Error("Invalid field name \"".concat(column, "\""));
|
|
108
|
+
}
|
|
109
|
+
astFragment = {
|
|
110
|
+
type: "column_ref",
|
|
111
|
+
schema: "public",
|
|
112
|
+
table: table,
|
|
113
|
+
column: column
|
|
114
|
+
};
|
|
115
|
+
break;
|
|
116
|
+
case isContext && isNumeric:
|
|
117
|
+
astFragment = {
|
|
118
|
+
as: null,
|
|
119
|
+
type: "cast",
|
|
120
|
+
expr: {
|
|
121
|
+
type: "function",
|
|
122
|
+
name: "current_setting",
|
|
123
|
+
args: {
|
|
124
|
+
type: "expr_list",
|
|
125
|
+
value: [
|
|
126
|
+
{
|
|
127
|
+
type: "parameter",
|
|
128
|
+
value: (0, escape_1.escapeLiteral)(value.replace(/^___yates_context_/, ""))
|
|
129
|
+
},
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
symbol: "::",
|
|
134
|
+
target: {
|
|
135
|
+
dataType: "float",
|
|
136
|
+
suffix: []
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
break;
|
|
140
|
+
case isContext && !isNumeric:
|
|
141
|
+
astFragment = {
|
|
142
|
+
type: "function",
|
|
143
|
+
name: "current_setting",
|
|
144
|
+
args: {
|
|
145
|
+
type: "expr_list",
|
|
146
|
+
value: [
|
|
147
|
+
{
|
|
148
|
+
type: "parameter",
|
|
149
|
+
value: (0, escape_1.escapeLiteral)(value.replace(/^___yates_context_/, ""))
|
|
150
|
+
},
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
break;
|
|
155
|
+
case isNumeric:
|
|
156
|
+
if (typeof value !== "number") {
|
|
157
|
+
throw new Error("Numeric fields can only be queried with numbers: querying field '".concat(field, "' with value '").concat(value, "'"));
|
|
158
|
+
}
|
|
159
|
+
astFragment = {
|
|
160
|
+
type: "number",
|
|
161
|
+
value: value
|
|
162
|
+
};
|
|
163
|
+
break;
|
|
164
|
+
// All other types are treated as strings
|
|
165
|
+
default:
|
|
166
|
+
astFragment = {
|
|
167
|
+
type: "parameter",
|
|
168
|
+
value: (0, escape_1.escapeLiteral)(value)
|
|
169
|
+
};
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
tokens[int] = {
|
|
173
|
+
astFragment: astFragment
|
|
174
|
+
};
|
|
175
|
+
where[field] = isNumeric ? int : "".concat(int);
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
tokens: tokens,
|
|
179
|
+
where: where
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
var expressionToSQL = function (getExpression, table) { return __awaiter(void 0, void 0, void 0, function () {
|
|
183
|
+
var baseClient, tokens, expressionClient, sql;
|
|
184
|
+
return __generator(this, function (_a) {
|
|
185
|
+
switch (_a.label) {
|
|
186
|
+
case 0:
|
|
187
|
+
if (typeof getExpression === "string") {
|
|
188
|
+
return [2 /*return*/, getExpression];
|
|
189
|
+
}
|
|
190
|
+
baseClient = new client_1.PrismaClient({
|
|
191
|
+
log: [{ level: "query", emit: "event" }]
|
|
192
|
+
});
|
|
193
|
+
tokens = {};
|
|
194
|
+
expressionClient = baseClient.$extends({
|
|
195
|
+
name: "expressionClient",
|
|
196
|
+
query: {
|
|
197
|
+
$allModels: {
|
|
198
|
+
$allOperations: function (_a) {
|
|
199
|
+
var model = _a.model, operation = _a.operation, args = _a.args, query = _a.query;
|
|
200
|
+
// if not findFirst or findUnique
|
|
201
|
+
if (operation !== "findFirst" && operation !== "findUnique") {
|
|
202
|
+
throw new Error('Only "findFirst" and "findUnique" are supported in client expressions');
|
|
203
|
+
}
|
|
204
|
+
if ("where" in args && args.where) {
|
|
205
|
+
var where = tokenizeWhereExpression(baseClient, args.where, table, model, tokens).where;
|
|
206
|
+
args.where = where;
|
|
207
|
+
}
|
|
208
|
+
return query(args);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
return [4 /*yield*/, new Promise(
|
|
214
|
+
// rome-ignore lint/suspicious/noAsyncPromiseExecutor: future cleanup
|
|
215
|
+
function (resolve, reject) { return __awaiter(void 0, void 0, void 0, function () {
|
|
216
|
+
var rawExpression, isSubselect, error_1;
|
|
217
|
+
return __generator(this, function (_a) {
|
|
218
|
+
switch (_a.label) {
|
|
219
|
+
case 0:
|
|
220
|
+
rawExpression = getExpression(expressionClient, expressionRowName, expressionContext);
|
|
221
|
+
isSubselect = typeof rawExpression === "object" && typeof rawExpression.then === "function";
|
|
222
|
+
expressionClient.$on("query", function (e) {
|
|
223
|
+
try {
|
|
224
|
+
var parser = new node_sql_parser_1.Parser();
|
|
225
|
+
// Parse the query into an AST
|
|
226
|
+
var ast = parser.astify(e.query, {
|
|
227
|
+
database: "postgresql"
|
|
228
|
+
});
|
|
229
|
+
var params = JSON.parse(e.params);
|
|
230
|
+
// By default Prisma will use a parameter for the limit, for Yates, the value is always "1"
|
|
231
|
+
ast.limit = { seperator: "", value: [{ type: "number", value: 1 }] };
|
|
232
|
+
// Now that the SQL has been generated, we can replace the tokens with the original values
|
|
233
|
+
for (var i = 0; i < params.length; i++) {
|
|
234
|
+
var param = params[i];
|
|
235
|
+
var token = tokens[param];
|
|
236
|
+
if (!token) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
var parameterizedStatement = deepFind(ast, {
|
|
240
|
+
right: {
|
|
241
|
+
type: "origin",
|
|
242
|
+
value: "$".concat(i + 1)
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
if (!parameterizedStatement) {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
parameterizedStatement.right = token.astFragment;
|
|
249
|
+
}
|
|
250
|
+
if (isSubselect) {
|
|
251
|
+
// For subselects, we need to convert the entire query and wrap in EXISTS so it converts to a binary expression
|
|
252
|
+
var subSelect = parser.sqlify(ast, {
|
|
253
|
+
database: "postgresql"
|
|
254
|
+
});
|
|
255
|
+
resolve("EXISTS(".concat(subSelect, ")"));
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
// For basic expressions, we're only interested in the WHERE clause and can convert just the WHERE clause into SQL
|
|
259
|
+
var where = parser.exprToSQL(ast.where, {
|
|
260
|
+
database: "postgresql"
|
|
261
|
+
});
|
|
262
|
+
resolve(where);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
reject(error);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
_a.label = 1;
|
|
270
|
+
case 1:
|
|
271
|
+
_a.trys.push([1, 6, , 7]);
|
|
272
|
+
if (!isSubselect) return [3 /*break*/, 3];
|
|
273
|
+
return [4 /*yield*/, rawExpression];
|
|
274
|
+
case 2:
|
|
275
|
+
_a.sent();
|
|
276
|
+
return [3 /*break*/, 5];
|
|
277
|
+
case 3: return [4 /*yield*/, expressionClient[table].findFirst({
|
|
278
|
+
where: rawExpression
|
|
279
|
+
})];
|
|
280
|
+
case 4:
|
|
281
|
+
_a.sent();
|
|
282
|
+
_a.label = 5;
|
|
283
|
+
case 5: return [3 /*break*/, 7];
|
|
284
|
+
case 6:
|
|
285
|
+
error_1 = _a.sent();
|
|
286
|
+
reject(error_1);
|
|
287
|
+
return [3 /*break*/, 7];
|
|
288
|
+
case 7: return [2 /*return*/];
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
}); })];
|
|
292
|
+
case 1:
|
|
293
|
+
sql = _a.sent();
|
|
294
|
+
// Close the client
|
|
295
|
+
return [4 /*yield*/, expressionClient.$disconnect()];
|
|
296
|
+
case 2:
|
|
297
|
+
// Close the client
|
|
298
|
+
_a.sent();
|
|
299
|
+
return [4 /*yield*/, baseClient.$disconnect()];
|
|
300
|
+
case 3:
|
|
301
|
+
_a.sent();
|
|
302
|
+
return [2 /*return*/, sql];
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
}); };
|
|
306
|
+
exports.expressionToSQL = expressionToSQL;
|
|
307
|
+
//# sourceMappingURL=expressions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expressions.js","sourceRoot":"","sources":["../src/expressions.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Prisma, PrismaClient } from "@prisma/client";
|
|
2
|
+
import { Expression } from "./expressions";
|
|
2
3
|
declare const VALID_OPERATIONS: readonly ["SELECT", "UPDATE", "INSERT", "DELETE"];
|
|
3
4
|
type Operation = typeof VALID_OPERATIONS[number];
|
|
4
5
|
export type Models = Prisma.ModelName;
|
|
5
|
-
export interface Ability {
|
|
6
|
+
export interface Ability<ContextKeys extends string = string> {
|
|
6
7
|
description?: string;
|
|
7
|
-
expression?:
|
|
8
|
+
expression?: Expression<ContextKeys>;
|
|
8
9
|
operation: Operation;
|
|
9
10
|
model?: Models;
|
|
10
11
|
slug?: string;
|
|
@@ -15,28 +16,33 @@ export type DefaultAbilities = {
|
|
|
15
16
|
[op in CRUDOperations]: Ability;
|
|
16
17
|
};
|
|
17
18
|
};
|
|
18
|
-
export type CustomAbilities = {
|
|
19
|
+
export type CustomAbilities<ContextKeys extends string = string> = {
|
|
19
20
|
[model in Models]?: {
|
|
20
|
-
[op in string]?: Ability
|
|
21
|
+
[op in string]?: Ability<ContextKeys>;
|
|
21
22
|
};
|
|
22
23
|
};
|
|
23
|
-
export type GetContextFn = () => {
|
|
24
|
+
export type GetContextFn<ContextKeys extends string = string> = () => {
|
|
24
25
|
role: string;
|
|
25
26
|
context?: {
|
|
26
|
-
[key
|
|
27
|
+
[key in ContextKeys]: string | number | string[];
|
|
27
28
|
};
|
|
28
29
|
} | null;
|
|
29
30
|
export declare const createAbilityName: (model: string, ability: string) => string;
|
|
30
31
|
export declare const createRoleName: (name: string) => string;
|
|
31
|
-
export declare const
|
|
32
|
-
|
|
32
|
+
export declare const createClient: (prisma: PrismaClient, getContext: GetContextFn) => Omit<PrismaClient<Prisma.PrismaClientOptions, never, Prisma.RejectOnNotFound | Prisma.RejectPerOperation | undefined, {
|
|
33
|
+
result: {} & Record<string, {}>;
|
|
34
|
+
model: {} & Record<string, {}>;
|
|
35
|
+
client: {};
|
|
36
|
+
query: {};
|
|
37
|
+
}>, "$use"> & {};
|
|
38
|
+
export declare const createRoles: <K extends CustomAbilities<string> = CustomAbilities<string>, T = DefaultAbilities & K>({ prisma, customAbilities, getRoles, }: {
|
|
33
39
|
prisma: PrismaClient;
|
|
34
40
|
customAbilities?: Partial<K> | undefined;
|
|
35
41
|
getRoles: (abilities: T) => {
|
|
36
|
-
[key: string]: Ability[]
|
|
42
|
+
[key: string]: "*" | Ability<string>[];
|
|
37
43
|
};
|
|
38
44
|
}) => Promise<void>;
|
|
39
|
-
export interface SetupParams<K extends CustomAbilities = CustomAbilities
|
|
45
|
+
export interface SetupParams<ContextKeys extends string = string, K extends CustomAbilities<ContextKeys> = CustomAbilities<ContextKeys>> {
|
|
40
46
|
/**
|
|
41
47
|
* The Prisma client instance. Used for database queries and model introspection.
|
|
42
48
|
*/
|
|
@@ -58,7 +64,15 @@ export interface SetupParams<K extends CustomAbilities = CustomAbilities> {
|
|
|
58
64
|
* You can also provide additional context here, which will be available in any RLS expressions you've defined.
|
|
59
65
|
* Returning `null` will result in the permissions being skipped entirely.
|
|
60
66
|
*/
|
|
61
|
-
getContext: GetContextFn
|
|
67
|
+
getContext: GetContextFn<ContextKeys>;
|
|
62
68
|
}
|
|
63
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Creates an extended client that sets contextual parameters and user role on every query
|
|
71
|
+
**/
|
|
72
|
+
export declare const setup: <ContextKeys extends string = string, K extends CustomAbilities<ContextKeys> = CustomAbilities<ContextKeys>>(params: SetupParams<ContextKeys, K>) => Promise<Omit<PrismaClient<Prisma.PrismaClientOptions, never, Prisma.RejectOnNotFound | Prisma.RejectPerOperation | undefined, {
|
|
73
|
+
result: {} & Record<string, {}>;
|
|
74
|
+
model: {} & Record<string, {}>;
|
|
75
|
+
client: {};
|
|
76
|
+
query: {};
|
|
77
|
+
}>, "$use"> & {}>;
|
|
64
78
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,33 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
50
50
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
|
+
var __values = (this && this.__values) || function(o) {
|
|
54
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
55
|
+
if (m) return m.call(o);
|
|
56
|
+
if (o && typeof o.length === "number") return {
|
|
57
|
+
next: function () {
|
|
58
|
+
if (o && i >= o.length) o = void 0;
|
|
59
|
+
return { value: o && o[i++], done: !o };
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
63
|
+
};
|
|
64
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
65
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
66
|
+
if (!m) return o;
|
|
67
|
+
var i = m.call(o), r, ar = [], e;
|
|
68
|
+
try {
|
|
69
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
70
|
+
}
|
|
71
|
+
catch (error) { e = { error: error }; }
|
|
72
|
+
finally {
|
|
73
|
+
try {
|
|
74
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
75
|
+
}
|
|
76
|
+
finally { if (e) throw e.error; }
|
|
77
|
+
}
|
|
78
|
+
return ar;
|
|
79
|
+
};
|
|
53
80
|
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
54
81
|
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
55
82
|
if (ar || !(i in from)) {
|
|
@@ -63,12 +90,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
63
90
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
64
91
|
};
|
|
65
92
|
exports.__esModule = true;
|
|
66
|
-
exports.setup = exports.createRoles = exports.
|
|
67
|
-
var client_1 = require("@prisma/client");
|
|
93
|
+
exports.setup = exports.createRoles = exports.createClient = exports.createRoleName = exports.createAbilityName = void 0;
|
|
68
94
|
var difference_1 = __importDefault(require("lodash/difference"));
|
|
69
95
|
var flatMap_1 = __importDefault(require("lodash/flatMap"));
|
|
70
96
|
var map_1 = __importDefault(require("lodash/map"));
|
|
71
97
|
var toPairs_1 = __importDefault(require("lodash/toPairs"));
|
|
98
|
+
var expressions_1 = require("./expressions");
|
|
72
99
|
var VALID_OPERATIONS = ["SELECT", "UPDATE", "INSERT", "DELETE"];
|
|
73
100
|
/**
|
|
74
101
|
* This function is used to take a lock that is automatically released at the end of the current transaction.
|
|
@@ -89,127 +116,131 @@ var createRoleName = function (name) {
|
|
|
89
116
|
return sanitizeSlug("yates_role_".concat(name));
|
|
90
117
|
};
|
|
91
118
|
exports.createRoleName = createRoleName;
|
|
92
|
-
// This
|
|
93
|
-
|
|
94
|
-
var
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
// This uses client extensions to set the role and context for the current user so that RLS can be applied
|
|
120
|
+
var createClient = function (prisma, getContext) {
|
|
121
|
+
var client = prisma.$extends({
|
|
122
|
+
name: "Yates client",
|
|
123
|
+
query: {
|
|
124
|
+
$allModels: {
|
|
125
|
+
$allOperations: function (_a) {
|
|
126
|
+
var _b;
|
|
127
|
+
var model = _a.model, args = _a.args, query = _a.query;
|
|
128
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
129
|
+
var ctx, role, context, pgRole, _c, _d, k, txResults, queryResults, e_1;
|
|
130
|
+
var e_2, _e;
|
|
131
|
+
return __generator(this, function (_f) {
|
|
132
|
+
switch (_f.label) {
|
|
133
|
+
case 0:
|
|
134
|
+
if (!model) {
|
|
135
|
+
return [2 /*return*/, query(args)];
|
|
136
|
+
}
|
|
137
|
+
ctx = getContext();
|
|
138
|
+
// If ctx is null, the middleware is explicitly skipped
|
|
139
|
+
if (ctx === null) {
|
|
140
|
+
return [2 /*return*/, query(args)];
|
|
141
|
+
}
|
|
142
|
+
role = ctx.role, context = ctx.context;
|
|
143
|
+
pgRole = (0, exports.createRoleName)(role);
|
|
144
|
+
if (context) {
|
|
145
|
+
try {
|
|
146
|
+
for (_c = __values(Object.keys(context)), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
147
|
+
k = _d.value;
|
|
148
|
+
if (!k.match(/^[a-z_\.]+$/)) {
|
|
149
|
+
throw new Error("Context variable \"".concat(k, "\" contains invalid characters. Context variables must only contain lowercase letters, numbers, periods and underscores."));
|
|
150
|
+
}
|
|
151
|
+
if (typeof context[k] !== "number" && typeof context[k] !== "string") {
|
|
152
|
+
throw new Error("Context variable \"".concat(k, "\" must be a string or number. Got ").concat(typeof context[k]));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
157
|
+
finally {
|
|
158
|
+
try {
|
|
159
|
+
if (_d && !_d.done && (_e = _c["return"])) _e.call(_c);
|
|
160
|
+
}
|
|
161
|
+
finally { if (e_2) throw e_2.error; }
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
_f.label = 1;
|
|
165
|
+
case 1:
|
|
166
|
+
_f.trys.push([1, 3, , 4]);
|
|
167
|
+
return [4 /*yield*/, prisma.$transaction(__spreadArray(__spreadArray([
|
|
168
|
+
// Switch to the user role, We can't use a prepared statement here, due to limitations in PG not allowing prepared statements to be used in SET ROLE
|
|
169
|
+
prisma.$queryRawUnsafe("SET ROLE ".concat(pgRole))
|
|
170
|
+
], __read((0, toPairs_1["default"])(context).map(function (_a) {
|
|
171
|
+
var _b = __read(_a, 2), key = _b[0], value = _b[1];
|
|
172
|
+
return prisma.$queryRaw(templateObject_1 || (templateObject_1 = __makeTemplateObject(["SELECT set_config(", ", ", ", true);"], ["SELECT set_config(", ", ", ", true);"])), key, value.toString());
|
|
173
|
+
})), false), [
|
|
174
|
+
// Now call original function
|
|
175
|
+
// Conveniently, the `query` function will happily run inside the transaction.
|
|
176
|
+
query(args),
|
|
177
|
+
// Switch role back to admin user
|
|
178
|
+
prisma.$queryRawUnsafe("SET ROLE none"),
|
|
179
|
+
], false))];
|
|
180
|
+
case 2:
|
|
181
|
+
txResults = _f.sent();
|
|
182
|
+
queryResults = txResults[txResults.length - 2];
|
|
183
|
+
return [2 /*return*/, queryResults];
|
|
184
|
+
case 3:
|
|
185
|
+
e_1 = _f.sent();
|
|
186
|
+
// Normalize RLS errors to make them a bit more readable.
|
|
187
|
+
if ((_b = e_1.message) === null || _b === void 0 ? void 0 : _b.includes("new row violates row-level security policy for table")) {
|
|
188
|
+
throw new Error("You do not have permission to perform this action.");
|
|
189
|
+
}
|
|
190
|
+
throw e_1;
|
|
191
|
+
case 4: return [2 /*return*/];
|
|
124
192
|
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
case 1:
|
|
129
|
-
_c.trys.push([1, 3, , 4]);
|
|
130
|
-
return [4 /*yield*/, adminClient.$transaction(__spreadArray(__spreadArray([
|
|
131
|
-
// Switch to the user role, We can't use a prepared statement here, due to limitations in PG not allowing prepared statements to be used in SET ROLE
|
|
132
|
-
adminClient.$queryRawUnsafe("SET ROLE ".concat(pgRole))
|
|
133
|
-
], (0, toPairs_1["default"])(context).map(function (_a) {
|
|
134
|
-
var key = _a[0], value = _a[1];
|
|
135
|
-
return adminClient.$queryRaw(templateObject_1 || (templateObject_1 = __makeTemplateObject(["SELECT set_config(", ", ", ", true);"], ["SELECT set_config(", ", ", ", true);"])), key, value);
|
|
136
|
-
}), true), [
|
|
137
|
-
// Now call original function
|
|
138
|
-
// Assumptions:
|
|
139
|
-
// - prisma model class is params.model in camelCase
|
|
140
|
-
// - prisma function name is params.action
|
|
141
|
-
adminClient[modelName][params.action](params.args),
|
|
142
|
-
// Switch role back to admin user
|
|
143
|
-
adminClient.$queryRawUnsafe("SET ROLE none"),
|
|
144
|
-
], false))];
|
|
145
|
-
case 2:
|
|
146
|
-
txResults = _c.sent();
|
|
147
|
-
queryResults = txResults[txResults.length - 2];
|
|
148
|
-
// This heuristic is used to determine if this is a query for a related entity, and if so, unwraps the results.
|
|
149
|
-
// This mimics the "native" prisma behaviour, where if you query for a related entity, it will return the related entity (or entities) directly, rather than an object with the related entity as a property.
|
|
150
|
-
// See https://prisma.slack.com/archives/CA491RJH0/p1674126834205399
|
|
151
|
-
if (params.args.select) {
|
|
152
|
-
selectKeys = Object.keys(params.args.select);
|
|
153
|
-
if (selectKeys.length === 1 &&
|
|
154
|
-
params.dataPath.length > 0 &&
|
|
155
|
-
selectKeys[0] === params.dataPath[params.dataPath.length - 1] &&
|
|
156
|
-
selectKeys[0] === Object.keys(queryResults)[0]) {
|
|
157
|
-
return [2 /*return*/, queryResults[selectKeys[0]]];
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return [2 /*return*/, queryResults];
|
|
161
|
-
case 3:
|
|
162
|
-
e_1 = _c.sent();
|
|
163
|
-
// Normalize RLS errors to make them a bit more readable.
|
|
164
|
-
if ((_b = e_1.message) === null || _b === void 0 ? void 0 : _b.includes("new row violates row-level security policy for table")) {
|
|
165
|
-
throw new Error("You do not have permission to perform this action.");
|
|
166
|
-
}
|
|
167
|
-
throw e_1;
|
|
168
|
-
case 4: return [2 /*return*/];
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
169
196
|
}
|
|
170
|
-
}
|
|
171
|
-
});
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
return client;
|
|
172
200
|
};
|
|
173
|
-
exports.
|
|
174
|
-
var setRLS = function (prisma, table, roleName, operation,
|
|
175
|
-
var policyName, rows;
|
|
201
|
+
exports.createClient = createClient;
|
|
202
|
+
var setRLS = function (prisma, table, roleName, operation, rawExpression) { return __awaiter(void 0, void 0, void 0, function () {
|
|
203
|
+
var expression, policyName, rows;
|
|
176
204
|
return __generator(this, function (_a) {
|
|
177
205
|
switch (_a.label) {
|
|
178
|
-
case 0:
|
|
206
|
+
case 0: return [4 /*yield*/, (0, expressions_1.expressionToSQL)(rawExpression, table)];
|
|
207
|
+
case 1:
|
|
208
|
+
expression = _a.sent();
|
|
179
209
|
policyName = "".concat(roleName, "_policy");
|
|
180
210
|
return [4 /*yield*/, prisma.$queryRawUnsafe("\n\t\tselect * from pg_catalog.pg_policies where tablename = '".concat(table, "' AND policyname = '").concat(policyName, "';\n\t"))];
|
|
181
|
-
case
|
|
211
|
+
case 2:
|
|
182
212
|
rows = _a.sent();
|
|
183
|
-
if (!(rows.length === 0)) return [3 /*break*/,
|
|
184
|
-
if (!(operation === "INSERT")) return [3 /*break*/,
|
|
213
|
+
if (!(rows.length === 0)) return [3 /*break*/, 7];
|
|
214
|
+
if (!(operation === "INSERT")) return [3 /*break*/, 4];
|
|
185
215
|
return [4 /*yield*/, prisma.$queryRawUnsafe("\n CREATE POLICY ".concat(policyName, " ON \"public\".\"").concat(table, "\" FOR ").concat(operation, " TO ").concat(roleName, " WITH CHECK (").concat(expression, ");\n "))];
|
|
186
|
-
case
|
|
216
|
+
case 3:
|
|
187
217
|
_a.sent();
|
|
188
|
-
return [3 /*break*/,
|
|
189
|
-
case
|
|
190
|
-
case
|
|
218
|
+
return [3 /*break*/, 6];
|
|
219
|
+
case 4: return [4 /*yield*/, prisma.$queryRawUnsafe("\n CREATE POLICY ".concat(policyName, " ON \"public\".\"").concat(table, "\" FOR ").concat(operation, " TO ").concat(roleName, " USING (").concat(expression, ");\n "))];
|
|
220
|
+
case 5:
|
|
191
221
|
_a.sent();
|
|
192
|
-
_a.label =
|
|
193
|
-
case
|
|
194
|
-
case 6:
|
|
195
|
-
if (!(rows[0].qual !== expression)) return [3 /*break*/, 10];
|
|
196
|
-
if (!(operation === "INSERT")) return [3 /*break*/, 8];
|
|
197
|
-
return [4 /*yield*/, prisma.$queryRawUnsafe("\n ALTER POLICY ".concat(policyName, " ON \"public\".\"").concat(table, "\" TO ").concat(roleName, " WITH CHECK (").concat(expression, ");\n "))];
|
|
222
|
+
_a.label = 6;
|
|
223
|
+
case 6: return [3 /*break*/, 11];
|
|
198
224
|
case 7:
|
|
225
|
+
if (!(rows[0].qual !== expression)) return [3 /*break*/, 11];
|
|
226
|
+
if (!(operation === "INSERT")) return [3 /*break*/, 9];
|
|
227
|
+
return [4 /*yield*/, prisma.$queryRawUnsafe("\n ALTER POLICY ".concat(policyName, " ON \"public\".\"").concat(table, "\" TO ").concat(roleName, " WITH CHECK (").concat(expression, ");\n "))];
|
|
228
|
+
case 8:
|
|
199
229
|
_a.sent();
|
|
200
|
-
return [3 /*break*/,
|
|
201
|
-
case
|
|
202
|
-
case
|
|
230
|
+
return [3 /*break*/, 11];
|
|
231
|
+
case 9: return [4 /*yield*/, prisma.$queryRawUnsafe("\n ALTER POLICY ".concat(policyName, " ON \"public\".\"").concat(table, "\" TO ").concat(roleName, " USING (").concat(expression, ");\n "))];
|
|
232
|
+
case 10:
|
|
203
233
|
_a.sent();
|
|
204
|
-
_a.label =
|
|
205
|
-
case
|
|
234
|
+
_a.label = 11;
|
|
235
|
+
case 11: return [2 /*return*/];
|
|
206
236
|
}
|
|
207
237
|
});
|
|
208
238
|
}); };
|
|
209
239
|
var createRoles = function (_a) {
|
|
210
240
|
var prisma = _a.prisma, customAbilities = _a.customAbilities, getRoles = _a.getRoles;
|
|
211
241
|
return __awaiter(void 0, void 0, void 0, function () {
|
|
212
|
-
var abilities, models, diff,
|
|
242
|
+
var abilities, models, diff, models_1, models_1_1, model, ability, operation, roles, _b, _c, _d, _i, model, table, _e, _f, _g, _h, slug, ability, roleName, _loop_1, _j, _k, _l, _m, key;
|
|
243
|
+
var e_3, _o;
|
|
213
244
|
var _p;
|
|
214
245
|
return __generator(this, function (_q) {
|
|
215
246
|
switch (_q.label) {
|
|
@@ -222,57 +253,66 @@ var createRoles = function (_a) {
|
|
|
222
253
|
throw new Error("Invalid models in custom abilities: ".concat(diff.join(", ")));
|
|
223
254
|
}
|
|
224
255
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
256
|
+
try {
|
|
257
|
+
for (models_1 = __values(models), models_1_1 = models_1.next(); !models_1_1.done; models_1_1 = models_1.next()) {
|
|
258
|
+
model = models_1_1.value;
|
|
259
|
+
abilities[model] = {
|
|
260
|
+
create: {
|
|
261
|
+
description: "Create ".concat(model),
|
|
262
|
+
expression: "true",
|
|
263
|
+
operation: "INSERT",
|
|
264
|
+
model: model,
|
|
265
|
+
slug: "create"
|
|
266
|
+
},
|
|
267
|
+
read: {
|
|
268
|
+
description: "Read ".concat(model),
|
|
269
|
+
expression: "true",
|
|
270
|
+
operation: "SELECT",
|
|
271
|
+
model: model,
|
|
272
|
+
slug: "read"
|
|
273
|
+
},
|
|
274
|
+
update: {
|
|
275
|
+
description: "Update ".concat(model),
|
|
276
|
+
expression: "true",
|
|
277
|
+
operation: "UPDATE",
|
|
278
|
+
model: model,
|
|
279
|
+
slug: "update"
|
|
280
|
+
},
|
|
281
|
+
"delete": {
|
|
282
|
+
description: "Delete ".concat(model),
|
|
283
|
+
expression: "true",
|
|
284
|
+
operation: "DELETE",
|
|
285
|
+
model: model,
|
|
286
|
+
slug: "delete"
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
if (customAbilities === null || customAbilities === void 0 ? void 0 : customAbilities[model]) {
|
|
290
|
+
for (ability in customAbilities[model]) {
|
|
291
|
+
operation = (_p = customAbilities[model][ability]) === null || _p === void 0 ? void 0 : _p.operation;
|
|
292
|
+
if (!operation)
|
|
293
|
+
continue;
|
|
294
|
+
abilities[model][ability] = __assign(__assign({}, customAbilities[model][ability]), { operation: operation, model: model, slug: ability });
|
|
295
|
+
}
|
|
263
296
|
}
|
|
264
297
|
}
|
|
265
298
|
}
|
|
299
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
300
|
+
finally {
|
|
301
|
+
try {
|
|
302
|
+
if (models_1_1 && !models_1_1.done && (_o = models_1["return"])) _o.call(models_1);
|
|
303
|
+
}
|
|
304
|
+
finally { if (e_3) throw e_3.error; }
|
|
305
|
+
}
|
|
266
306
|
roles = getRoles(abilities);
|
|
267
307
|
_b = abilities;
|
|
268
308
|
_c = [];
|
|
269
309
|
for (_d in _b)
|
|
270
310
|
_c.push(_d);
|
|
271
|
-
|
|
311
|
+
_i = 0;
|
|
272
312
|
_q.label = 1;
|
|
273
313
|
case 1:
|
|
274
|
-
if (!(
|
|
275
|
-
_d = _c[
|
|
314
|
+
if (!(_i < _c.length)) return [3 /*break*/, 8];
|
|
315
|
+
_d = _c[_i];
|
|
276
316
|
if (!(_d in _b)) return [3 /*break*/, 7];
|
|
277
317
|
model = _d;
|
|
278
318
|
table = model;
|
|
@@ -282,17 +322,17 @@ var createRoles = function (_a) {
|
|
|
282
322
|
])];
|
|
283
323
|
case 2:
|
|
284
324
|
_q.sent();
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
for (
|
|
288
|
-
|
|
289
|
-
|
|
325
|
+
_e = abilities[model];
|
|
326
|
+
_f = [];
|
|
327
|
+
for (_g in _e)
|
|
328
|
+
_f.push(_g);
|
|
329
|
+
_h = 0;
|
|
290
330
|
_q.label = 3;
|
|
291
331
|
case 3:
|
|
292
|
-
if (!(
|
|
293
|
-
|
|
294
|
-
if (!(
|
|
295
|
-
slug =
|
|
332
|
+
if (!(_h < _f.length)) return [3 /*break*/, 7];
|
|
333
|
+
_g = _f[_h];
|
|
334
|
+
if (!(_g in _e)) return [3 /*break*/, 6];
|
|
335
|
+
slug = _g;
|
|
296
336
|
ability = abilities[model][slug];
|
|
297
337
|
if (!VALID_OPERATIONS.includes(ability.operation)) {
|
|
298
338
|
throw new Error("Invalid operation: ".concat(ability.operation));
|
|
@@ -313,10 +353,10 @@ var createRoles = function (_a) {
|
|
|
313
353
|
_q.sent();
|
|
314
354
|
_q.label = 6;
|
|
315
355
|
case 6:
|
|
316
|
-
|
|
356
|
+
_h++;
|
|
317
357
|
return [3 /*break*/, 3];
|
|
318
358
|
case 7:
|
|
319
|
-
|
|
359
|
+
_i++;
|
|
320
360
|
return [3 /*break*/, 1];
|
|
321
361
|
case 8:
|
|
322
362
|
_loop_1 = function (key) {
|
|
@@ -371,23 +411,23 @@ var createRoles = function (_a) {
|
|
|
371
411
|
}
|
|
372
412
|
});
|
|
373
413
|
};
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
for (
|
|
377
|
-
|
|
378
|
-
|
|
414
|
+
_j = roles;
|
|
415
|
+
_k = [];
|
|
416
|
+
for (_l in _j)
|
|
417
|
+
_k.push(_l);
|
|
418
|
+
_m = 0;
|
|
379
419
|
_q.label = 9;
|
|
380
420
|
case 9:
|
|
381
|
-
if (!(
|
|
382
|
-
|
|
383
|
-
if (!(
|
|
384
|
-
key =
|
|
421
|
+
if (!(_m < _k.length)) return [3 /*break*/, 12];
|
|
422
|
+
_l = _k[_m];
|
|
423
|
+
if (!(_l in _j)) return [3 /*break*/, 11];
|
|
424
|
+
key = _l;
|
|
385
425
|
return [5 /*yield**/, _loop_1(key)];
|
|
386
426
|
case 10:
|
|
387
427
|
_q.sent();
|
|
388
428
|
_q.label = 11;
|
|
389
429
|
case 11:
|
|
390
|
-
|
|
430
|
+
_m++;
|
|
391
431
|
return [3 /*break*/, 9];
|
|
392
432
|
case 12: return [2 /*return*/];
|
|
393
433
|
}
|
|
@@ -395,8 +435,11 @@ var createRoles = function (_a) {
|
|
|
395
435
|
});
|
|
396
436
|
};
|
|
397
437
|
exports.createRoles = createRoles;
|
|
438
|
+
/**
|
|
439
|
+
* Creates an extended client that sets contextual parameters and user role on every query
|
|
440
|
+
**/
|
|
398
441
|
var setup = function (params) { return __awaiter(void 0, void 0, void 0, function () {
|
|
399
|
-
var prisma, customAbilities, getRoles, getContext;
|
|
442
|
+
var prisma, customAbilities, getRoles, getContext, client;
|
|
400
443
|
return __generator(this, function (_a) {
|
|
401
444
|
switch (_a.label) {
|
|
402
445
|
case 0:
|
|
@@ -404,8 +447,8 @@ var setup = function (params) { return __awaiter(void 0, void 0, void 0, functio
|
|
|
404
447
|
return [4 /*yield*/, (0, exports.createRoles)({ prisma: prisma, customAbilities: customAbilities, getRoles: getRoles })];
|
|
405
448
|
case 1:
|
|
406
449
|
_a.sent();
|
|
407
|
-
(0, exports.
|
|
408
|
-
return [2 /*return
|
|
450
|
+
client = (0, exports.createClient)(prisma, getContext);
|
|
451
|
+
return [2 /*return*/, client];
|
|
409
452
|
}
|
|
410
453
|
});
|
|
411
454
|
}); };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cerebruminc/yates",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Role based access control for Prisma Apps",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -29,10 +29,11 @@
|
|
|
29
29
|
"uuid": "^9.0.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
+
"@lucianbuzzo/node-sql-parser": "^4.6.6",
|
|
32
33
|
"lodash": "^4.17.21"
|
|
33
34
|
},
|
|
34
35
|
"peerDependencies": {
|
|
35
36
|
"@prisma/client": "^4.0.0",
|
|
36
37
|
"prisma": "^4.9.0"
|
|
37
38
|
}
|
|
38
|
-
}
|
|
39
|
+
}
|