@teamkeel/functions-runtime 0.298.0 → 0.300.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/package.json +1 -1
- package/src/handleRequest.js +2 -1
- package/src/handleRequest.test.js +34 -0
- package/src/permissions.js +13 -2
- package/src/permissions.test.js +23 -1
package/package.json
CHANGED
package/src/handleRequest.js
CHANGED
|
@@ -47,6 +47,7 @@ async function handleRequest(request, config) {
|
|
|
47
47
|
const result = await db.transaction().execute(async (transaction) => {
|
|
48
48
|
const ctx = createContextAPI(request.meta);
|
|
49
49
|
const api = createFunctionAPI({
|
|
50
|
+
meta: request.meta,
|
|
50
51
|
headers,
|
|
51
52
|
db: transaction,
|
|
52
53
|
});
|
|
@@ -56,7 +57,7 @@ async function handleRequest(request, config) {
|
|
|
56
57
|
// Call the user's custom function!
|
|
57
58
|
const fnResult = await customFunction(request.params, api, ctx);
|
|
58
59
|
|
|
59
|
-
// api.permissions maintains an internal state of whether the current operation has been *explicitly* permitted/denied by the user in the course of their custom function.
|
|
60
|
+
// api.permissions maintains an internal state of whether the current operation has been *explicitly* permitted/denied by the user in the course of their custom function, or if execution has already been permitted by a role based permission (evaluated in the main runtime).
|
|
60
61
|
// we need to check that the final state is permitted or unpermitted. if it's not, then it means that the user has taken no explicit action to permit/deny
|
|
61
62
|
// and therefore we default to checking the permissions defined in the schema automatically.
|
|
62
63
|
switch (api.permissions.getState()) {
|
|
@@ -140,6 +140,40 @@ test("when there is an unexpected error in the custom function", async () => {
|
|
|
140
140
|
});
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
+
test("when a role based permission has already been granted by the main runtime", async () => {
|
|
144
|
+
const config = {
|
|
145
|
+
functions: {
|
|
146
|
+
createPost: async (inputs, api, ctx) => {
|
|
147
|
+
return {
|
|
148
|
+
title: inputs.title,
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
actionTypes: {
|
|
153
|
+
createPost: PROTO_ACTION_TYPES.CREATE,
|
|
154
|
+
},
|
|
155
|
+
createFunctionAPI: ({ headers, db }) => {
|
|
156
|
+
return {
|
|
157
|
+
permissions: new Permissions({ status: "granted", reason: "role" }),
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
createContextAPI: () => {},
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const rpcReq = createJSONRPCRequest("123", "createPost", { title: "a post" });
|
|
164
|
+
|
|
165
|
+
expect(await handleRequest(rpcReq, config)).toEqual({
|
|
166
|
+
id: "123",
|
|
167
|
+
jsonrpc: "2.0",
|
|
168
|
+
result: {
|
|
169
|
+
title: "a post",
|
|
170
|
+
},
|
|
171
|
+
meta: {
|
|
172
|
+
headers: {},
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
143
177
|
test("when there is an unexpected object thrown in the custom function", async () => {
|
|
144
178
|
const config = {
|
|
145
179
|
functions: {
|
package/src/permissions.js
CHANGED
|
@@ -6,12 +6,23 @@ const PERMISSION_STATE = {
|
|
|
6
6
|
UNPERMITTED: "unpermitted",
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
const defaultState = {
|
|
10
|
+
status: "unknown",
|
|
11
|
+
};
|
|
12
|
+
|
|
9
13
|
class Permissions {
|
|
10
|
-
|
|
14
|
+
// The permissionState here is the prior state passed in from the Go runtime
|
|
15
|
+
// The Go runtime performs role based permission rule checks prior to calling the functions
|
|
16
|
+
// runtime, so the status could already be granted. If already granted, then we need to inherit that permission state as the state is later used to decide whether to run in process permission checks
|
|
17
|
+
// TLDR if a role based permission is relevant and it is granted, then it is effectively the same as the end user calling api.permissions.allow() explicitly in terms of behaviour.
|
|
18
|
+
constructor(permissionState = defaultState) {
|
|
11
19
|
this.state = {
|
|
12
20
|
// permitted starts off as null to indicate that the end user
|
|
13
21
|
// hasn't explicitly marked a function execution as permitted yet
|
|
14
|
-
permitted:
|
|
22
|
+
permitted:
|
|
23
|
+
permissionState !== null && permissionState.status === "granted"
|
|
24
|
+
? true
|
|
25
|
+
: null,
|
|
15
26
|
};
|
|
16
27
|
}
|
|
17
28
|
|
package/src/permissions.test.js
CHANGED
|
@@ -14,10 +14,13 @@ process.env.KEEL_DB_CONN = `postgresql://postgres:postgres@localhost:5432/functi
|
|
|
14
14
|
let permissions;
|
|
15
15
|
let ctx = {};
|
|
16
16
|
let db = getDatabase();
|
|
17
|
+
let permissionState = {
|
|
18
|
+
status: "unknown",
|
|
19
|
+
};
|
|
17
20
|
|
|
18
21
|
describe("explicit", () => {
|
|
19
22
|
beforeEach(() => {
|
|
20
|
-
permissions = new Permissions();
|
|
23
|
+
permissions = new Permissions(permissionState);
|
|
21
24
|
});
|
|
22
25
|
|
|
23
26
|
test("explicitly allowing execution", () => {
|
|
@@ -37,6 +40,25 @@ describe("explicit", () => {
|
|
|
37
40
|
});
|
|
38
41
|
});
|
|
39
42
|
|
|
43
|
+
describe("prior state", () => {
|
|
44
|
+
test("when the prior state is unknown", () => {
|
|
45
|
+
permissions = new Permissions({
|
|
46
|
+
status: "unknown",
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(permissions.getState()).toEqual(PERMISSION_STATE.UNKNOWN);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("when the prior state is granted", () => {
|
|
53
|
+
permissions = new Permissions({
|
|
54
|
+
status: "granted",
|
|
55
|
+
reason: "role",
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
expect(permissions.getState()).toEqual(PERMISSION_STATE.PERMITTED);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
40
62
|
describe("check", () => {
|
|
41
63
|
const functionName = "createPerson";
|
|
42
64
|
|