@pikku/core 0.11.0 → 0.11.2
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 +35 -1
- package/dist/errors/error-handler.js +3 -3
- package/dist/errors/errors.d.ts +1 -1
- package/dist/errors/errors.js +3 -3
- package/dist/function/function-runner.d.ts +8 -8
- package/dist/function/function-runner.js +88 -23
- package/dist/function/functions.types.d.ts +104 -33
- package/dist/function/functions.types.js +50 -7
- package/dist/handle-error.d.ts +1 -1
- package/dist/handle-error.js +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +5 -2
- package/dist/middleware/auth-apikey.d.ts +1 -1
- package/dist/middleware/auth-apikey.js +3 -3
- package/dist/middleware/auth-bearer.d.ts +1 -1
- package/dist/middleware/auth-bearer.js +3 -3
- package/dist/middleware/auth-cookie.d.ts +1 -1
- package/dist/middleware/auth-cookie.js +5 -5
- package/dist/middleware/timeout.d.ts +1 -1
- package/dist/middleware/timeout.js +3 -2
- package/dist/middleware-runner.d.ts +6 -20
- package/dist/middleware-runner.js +19 -20
- package/dist/permissions.d.ts +6 -89
- package/dist/permissions.js +24 -81
- package/dist/pikku-state.d.ts +32 -104
- package/dist/pikku-state.js +140 -66
- package/dist/schema.d.ts +8 -6
- package/dist/schema.js +25 -13
- package/dist/services/user-session-service.d.ts +7 -3
- package/dist/services/user-session-service.js +8 -1
- package/dist/services/workflow-service.d.ts +38 -0
- package/dist/services/workflow-service.js +1 -0
- package/dist/types/core.types.d.ts +40 -41
- package/dist/types/core.types.js +4 -15
- package/dist/types/state.types.d.ts +135 -0
- package/dist/types/state.types.js +1 -0
- package/dist/utils.d.ts +9 -1
- package/dist/utils.js +48 -3
- package/dist/wirings/channel/channel-common.js +2 -3
- package/dist/wirings/channel/channel-handler.d.ts +3 -2
- package/dist/wirings/channel/channel-handler.js +7 -10
- package/dist/wirings/channel/channel-runner.d.ts +2 -2
- package/dist/wirings/channel/channel-runner.js +5 -5
- package/dist/wirings/channel/channel.types.d.ts +10 -5
- package/dist/wirings/channel/local/local-channel-runner.d.ts +1 -1
- package/dist/wirings/channel/local/local-channel-runner.js +19 -26
- package/dist/wirings/channel/log-channels.js +1 -1
- package/dist/wirings/channel/serverless/serverless-channel-runner.d.ts +1 -1
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +43 -48
- package/dist/wirings/cli/channel/cli-channel-runner.js +3 -2
- package/dist/wirings/cli/cli-runner.d.ts +5 -5
- package/dist/wirings/cli/cli-runner.js +20 -38
- package/dist/wirings/cli/cli.types.d.ts +16 -11
- package/dist/wirings/forge-node/forge-node.types.d.ts +120 -0
- package/dist/wirings/forge-node/forge-node.types.js +38 -0
- package/dist/wirings/forge-node/index.d.ts +1 -0
- package/dist/wirings/forge-node/index.js +1 -0
- package/dist/wirings/http/http-runner.d.ts +8 -8
- package/dist/wirings/http/http-runner.js +46 -51
- package/dist/wirings/http/http.types.d.ts +19 -11
- package/dist/wirings/http/log-http-routes.js +1 -1
- package/dist/wirings/http/routers/path-to-regex.js +2 -2
- package/dist/wirings/mcp/mcp-runner.d.ts +2 -2
- package/dist/wirings/mcp/mcp-runner.js +33 -40
- package/dist/wirings/mcp/mcp.types.d.ts +6 -0
- package/dist/wirings/queue/queue-runner.d.ts +3 -3
- package/dist/wirings/queue/queue-runner.js +13 -23
- package/dist/wirings/queue/queue.types.d.ts +4 -11
- package/dist/wirings/rpc/index.d.ts +1 -1
- package/dist/wirings/rpc/index.js +1 -1
- package/dist/wirings/rpc/rpc-runner.d.ts +17 -16
- package/dist/wirings/rpc/rpc-runner.js +98 -61
- package/dist/wirings/rpc/rpc-types.d.ts +7 -0
- package/dist/wirings/scheduler/log-schedulers.js +1 -1
- package/dist/wirings/scheduler/scheduler-runner.d.ts +3 -3
- package/dist/wirings/scheduler/scheduler-runner.js +18 -29
- package/dist/wirings/scheduler/scheduler.types.d.ts +3 -8
- package/dist/wirings/trigger/index.d.ts +2 -0
- package/dist/wirings/trigger/index.js +2 -0
- package/dist/wirings/trigger/trigger-runner.d.ts +29 -0
- package/dist/wirings/trigger/trigger-runner.js +57 -0
- package/dist/wirings/trigger/trigger.types.d.ts +42 -0
- package/dist/wirings/trigger/trigger.types.js +1 -0
- package/dist/wirings/workflow/dsl/index.d.ts +5 -0
- package/dist/wirings/workflow/dsl/index.js +4 -0
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +286 -0
- package/dist/wirings/workflow/dsl/workflow-dsl.types.js +5 -0
- package/dist/wirings/workflow/dsl/workflow-runner.d.ts +5 -0
- package/dist/wirings/workflow/dsl/workflow-runner.js +22 -0
- package/dist/wirings/workflow/graph/graph-node.d.ts +122 -0
- package/dist/wirings/workflow/graph/graph-node.js +58 -0
- package/dist/wirings/workflow/graph/graph-runner.d.ts +35 -0
- package/dist/wirings/workflow/graph/graph-runner.js +452 -0
- package/dist/wirings/workflow/graph/index.d.ts +3 -0
- package/dist/wirings/workflow/graph/index.js +3 -0
- package/dist/wirings/workflow/graph/workflow-graph.types.d.ts +95 -0
- package/dist/wirings/workflow/graph/workflow-graph.types.js +15 -0
- package/dist/wirings/workflow/index.d.ts +7 -3
- package/dist/wirings/workflow/index.js +6 -3
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +108 -10
- package/dist/wirings/workflow/pikku-workflow-service.js +274 -165
- package/dist/wirings/workflow/wire-workflow.d.ts +42 -0
- package/dist/wirings/workflow/wire-workflow.js +53 -0
- package/dist/wirings/workflow/workflow-utils.d.ts +23 -0
- package/dist/wirings/workflow/workflow-utils.js +66 -0
- package/dist/wirings/workflow/workflow.types.d.ts +135 -128
- package/package.json +11 -2
- package/src/errors/error-handler.ts +3 -3
- package/src/errors/errors.ts +3 -3
- package/src/factory-functions.test.ts +9 -36
- package/src/function/function-runner.test.ts +58 -69
- package/src/function/function-runner.ts +130 -37
- package/src/function/functions.types.ts +193 -54
- package/src/handle-error.ts +1 -1
- package/src/index.ts +10 -5
- package/src/middleware/auth-apikey.test.ts +360 -0
- package/src/middleware/auth-apikey.ts +3 -7
- package/src/middleware/auth-bearer.test.ts +447 -0
- package/src/middleware/auth-bearer.ts +27 -33
- package/src/middleware/auth-cookie.test.ts +525 -0
- package/src/middleware/auth-cookie.ts +6 -6
- package/src/middleware/timeout.ts +4 -2
- package/src/middleware-runner.test.ts +58 -127
- package/src/middleware-runner.ts +29 -25
- package/src/permissions.test.ts +68 -130
- package/src/permissions.ts +41 -100
- package/src/pikku-state.ts +156 -201
- package/src/schema.ts +42 -13
- package/src/services/user-session-service.ts +14 -4
- package/src/services/workflow-service.ts +74 -0
- package/src/types/core.types.ts +79 -58
- package/src/types/state.types.ts +195 -0
- package/src/utils.ts +59 -4
- package/src/wirings/channel/channel-common.ts +4 -7
- package/src/wirings/channel/channel-handler.ts +17 -23
- package/src/wirings/channel/channel-runner.ts +7 -7
- package/src/wirings/channel/channel.types.ts +14 -11
- package/src/wirings/channel/local/local-channel-runner.test.ts +8 -4
- package/src/wirings/channel/local/local-channel-runner.ts +23 -38
- package/src/wirings/channel/log-channels.ts +1 -1
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +53 -79
- package/src/wirings/cli/channel/cli-channel-runner.ts +4 -3
- package/src/wirings/cli/cli-runner.test.ts +26 -26
- package/src/wirings/cli/cli-runner.ts +34 -71
- package/src/wirings/cli/cli.types.ts +23 -16
- package/src/wirings/forge-node/forge-node.types.ts +135 -0
- package/src/wirings/forge-node/index.ts +1 -0
- package/src/wirings/http/http-runner.test.ts +10 -10
- package/src/wirings/http/http-runner.ts +56 -66
- package/src/wirings/http/http.types.ts +19 -14
- package/src/wirings/http/log-http-routes.ts +1 -1
- package/src/wirings/http/routers/path-to-regex.test.ts +30 -19
- package/src/wirings/http/routers/path-to-regex.ts +2 -2
- package/src/wirings/mcp/mcp-runner.ts +44 -69
- package/src/wirings/mcp/mcp.types.ts +6 -0
- package/src/wirings/queue/queue-runner.test.ts +641 -0
- package/src/wirings/queue/queue-runner.ts +16 -38
- package/src/wirings/queue/queue.types.ts +4 -11
- package/src/wirings/rpc/index.ts +1 -1
- package/src/wirings/rpc/rpc-runner.ts +129 -77
- package/src/wirings/rpc/rpc-types.ts +8 -0
- package/src/wirings/scheduler/log-schedulers.ts +1 -1
- package/src/wirings/scheduler/scheduler-runner.test.ts +627 -0
- package/src/wirings/scheduler/scheduler-runner.ts +29 -55
- package/src/wirings/scheduler/scheduler.types.ts +3 -9
- package/src/wirings/trigger/index.ts +2 -0
- package/src/wirings/trigger/trigger-runner.ts +96 -0
- package/src/wirings/trigger/trigger.types.ts +56 -0
- package/src/wirings/workflow/dsl/index.ts +30 -0
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +312 -0
- package/src/wirings/workflow/dsl/workflow-runner.ts +27 -0
- package/src/wirings/workflow/graph/graph-node.ts +227 -0
- package/src/wirings/workflow/graph/graph-runner.ts +694 -0
- package/src/wirings/workflow/graph/index.ts +3 -0
- package/src/wirings/workflow/graph/workflow-graph.types.ts +126 -0
- package/src/wirings/workflow/index.ts +60 -12
- package/src/wirings/workflow/pikku-workflow-service.ts +401 -195
- package/src/wirings/workflow/wire-workflow.ts +94 -0
- package/src/wirings/workflow/workflow-utils.ts +120 -0
- package/src/wirings/workflow/workflow.types.ts +167 -188
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/wirings/workflow/workflow-runner.d.ts +0 -35
- package/dist/wirings/workflow/workflow-runner.js +0 -68
- package/src/wirings/workflow/workflow-runner.ts +0 -85
package/src/permissions.test.ts
CHANGED
|
@@ -1,22 +1,39 @@
|
|
|
1
1
|
import { describe, test, beforeEach } from 'node:test'
|
|
2
2
|
import * as assert from 'node:assert'
|
|
3
|
+
import { addPermission, runPermissions } from './permissions.js'
|
|
4
|
+
import { pikkuState, resetPikkuState } from './pikku-state.js'
|
|
5
|
+
import { CoreServices, CoreUserSession } from './types/core.types.js'
|
|
3
6
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from './permissions.js'
|
|
8
|
-
import { resetPikkuState } from './pikku-state.js'
|
|
9
|
-
import {
|
|
10
|
-
CoreServices,
|
|
11
|
-
CoreUserSession,
|
|
12
|
-
PikkuWiringTypes,
|
|
13
|
-
} from './types/core.types.js'
|
|
14
|
-
import { CorePermissionGroup } from './function/functions.types.js'
|
|
7
|
+
CorePermissionGroup,
|
|
8
|
+
CorePikkuPermission,
|
|
9
|
+
} from './function/functions.types.js'
|
|
15
10
|
|
|
16
11
|
beforeEach(() => {
|
|
17
12
|
resetPikkuState()
|
|
18
13
|
})
|
|
19
14
|
|
|
15
|
+
// Test helper to get permissions for tags (not exported from core to keep it lean)
|
|
16
|
+
const getPermissionsForTags = (
|
|
17
|
+
tags?: string[]
|
|
18
|
+
): (CorePermissionGroup | CorePikkuPermission)[] => {
|
|
19
|
+
if (!tags || tags.length === 0) {
|
|
20
|
+
return []
|
|
21
|
+
}
|
|
22
|
+
const permissionsStore = pikkuState(null, 'permissions', 'tagGroup')
|
|
23
|
+
const result: (CorePermissionGroup | CorePikkuPermission)[] = []
|
|
24
|
+
for (const tag of new Set(tags)) {
|
|
25
|
+
const tagPermissions = permissionsStore[tag]
|
|
26
|
+
if (tagPermissions) {
|
|
27
|
+
if (Array.isArray(tagPermissions)) {
|
|
28
|
+
result.push(...tagPermissions)
|
|
29
|
+
} else {
|
|
30
|
+
result.push(tagPermissions)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return result
|
|
35
|
+
}
|
|
36
|
+
|
|
20
37
|
const mockServices: CoreServices = {
|
|
21
38
|
logger: {
|
|
22
39
|
info: () => {},
|
|
@@ -82,92 +99,13 @@ describe('addPermission', () => {
|
|
|
82
99
|
})
|
|
83
100
|
})
|
|
84
101
|
|
|
85
|
-
describe('getPermissionsForTags', () => {
|
|
86
|
-
test('should return empty array when no tags provided', () => {
|
|
87
|
-
const permissions = getPermissionsForTags([])
|
|
88
|
-
assert.deepEqual(permissions, [])
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
test('should return empty array when tags is undefined', () => {
|
|
92
|
-
const permissions = getPermissionsForTags(undefined)
|
|
93
|
-
assert.deepEqual(permissions, [])
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
test('should return permissions for single tag', () => {
|
|
97
|
-
const mockPermission = async () => true
|
|
98
|
-
addPermission('singleTestTag', [mockPermission])
|
|
99
|
-
|
|
100
|
-
const permissions = getPermissionsForTags(['singleTestTag'])
|
|
101
|
-
assert.equal(permissions.length, 1)
|
|
102
|
-
assert.equal(permissions[0], mockPermission)
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
test('should return permissions for multiple tags', () => {
|
|
106
|
-
const mockPermission1 = async () => true
|
|
107
|
-
const mockPermission2 = async () => false
|
|
108
|
-
|
|
109
|
-
addPermission('tag1', [mockPermission1])
|
|
110
|
-
addPermission('tag2', [mockPermission2])
|
|
111
|
-
|
|
112
|
-
const permissions = getPermissionsForTags(['tag1', 'tag2'])
|
|
113
|
-
assert.equal(permissions.length, 2)
|
|
114
|
-
assert.equal(permissions[0], mockPermission1)
|
|
115
|
-
assert.equal(permissions[1], mockPermission2)
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
test('should handle array permissions correctly', () => {
|
|
119
|
-
const mockPermission1 = async () => true
|
|
120
|
-
const mockPermission2 = async () => false
|
|
121
|
-
|
|
122
|
-
addPermission('arrayHandleTestTag', [mockPermission1, mockPermission2])
|
|
123
|
-
|
|
124
|
-
const permissions = getPermissionsForTags(['arrayHandleTestTag'])
|
|
125
|
-
assert.equal(permissions.length, 2)
|
|
126
|
-
assert.equal(permissions[0], mockPermission1)
|
|
127
|
-
assert.equal(permissions[1], mockPermission2)
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
test('should handle non-array permissions correctly', () => {
|
|
131
|
-
const mockPermissionGroup: CorePermissionGroup = {
|
|
132
|
-
permissions: [async () => true],
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
addPermission('objectHandleTestTag', mockPermissionGroup)
|
|
136
|
-
|
|
137
|
-
const permissions = getPermissionsForTags(['objectHandleTestTag'])
|
|
138
|
-
assert.equal(permissions.length, 1)
|
|
139
|
-
assert.equal(permissions[0], mockPermissionGroup)
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
test('should deduplicate tags', () => {
|
|
143
|
-
const mockPermission = async () => true
|
|
144
|
-
addPermission('dedupeTestTag', [mockPermission])
|
|
145
|
-
|
|
146
|
-
const permissions = getPermissionsForTags([
|
|
147
|
-
'dedupeTestTag',
|
|
148
|
-
'dedupeTestTag',
|
|
149
|
-
])
|
|
150
|
-
assert.equal(permissions.length, 1)
|
|
151
|
-
assert.equal(permissions[0], mockPermission)
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
test('should ignore non-existent tags', () => {
|
|
155
|
-
const mockPermission = async () => true
|
|
156
|
-
addPermission('existingTag', [mockPermission])
|
|
157
|
-
|
|
158
|
-
const permissions = getPermissionsForTags(['existingTag', 'nonExistentTag'])
|
|
159
|
-
assert.equal(permissions.length, 1)
|
|
160
|
-
assert.equal(permissions[0], mockPermission)
|
|
161
|
-
})
|
|
162
|
-
})
|
|
163
|
-
|
|
164
102
|
describe('runPermissions', () => {
|
|
165
103
|
test('should pass when no permissions are defined', async () => {
|
|
166
104
|
// Should not throw
|
|
167
|
-
await runPermissions(
|
|
168
|
-
|
|
105
|
+
await runPermissions('rpc', Math.random().toString(), {
|
|
106
|
+
services: mockServices,
|
|
107
|
+
wire: {},
|
|
169
108
|
data: {},
|
|
170
|
-
session: mockSession,
|
|
171
109
|
})
|
|
172
110
|
})
|
|
173
111
|
|
|
@@ -180,11 +118,11 @@ describe('runPermissions', () => {
|
|
|
180
118
|
|
|
181
119
|
addPermission('wiringTag', [wiringTagPermission])
|
|
182
120
|
|
|
183
|
-
await runPermissions(
|
|
121
|
+
await runPermissions('rpc', Math.random().toString(), {
|
|
184
122
|
wireInheritedPermissions: [{ type: 'tag', tag: 'wiringTag' }],
|
|
185
|
-
|
|
123
|
+
services: mockServices,
|
|
124
|
+
wire: {},
|
|
186
125
|
data: {},
|
|
187
|
-
session: mockSession,
|
|
188
126
|
})
|
|
189
127
|
|
|
190
128
|
assert.deepEqual(executionOrder, ['wiringTag'])
|
|
@@ -223,14 +161,14 @@ describe('runPermissions', () => {
|
|
|
223
161
|
],
|
|
224
162
|
}
|
|
225
163
|
|
|
226
|
-
await runPermissions(
|
|
164
|
+
await runPermissions('rpc', Math.random().toString(), {
|
|
227
165
|
wireInheritedPermissions: [{ type: 'tag', tag: 'wiringTag' }],
|
|
228
166
|
wirePermissions,
|
|
229
167
|
funcInheritedPermissions: [{ type: 'tag', tag: 'funcTag' }],
|
|
230
168
|
funcPermissions,
|
|
231
|
-
|
|
169
|
+
services: mockServices,
|
|
170
|
+
wire: {},
|
|
232
171
|
data: {},
|
|
233
|
-
session: mockSession,
|
|
234
172
|
})
|
|
235
173
|
|
|
236
174
|
// Order: wireInheritedPermissions (wiringTag) → wirePermissions → funcInheritedPermissions (funcTag) → funcPermissions
|
|
@@ -249,11 +187,11 @@ describe('runPermissions', () => {
|
|
|
249
187
|
addPermission('atLeastOneTestTag', [failingPermission, passingPermission])
|
|
250
188
|
|
|
251
189
|
// Should not throw because at least one permission passes
|
|
252
|
-
await runPermissions(
|
|
190
|
+
await runPermissions('rpc', Math.random().toString(), {
|
|
253
191
|
wireInheritedPermissions: [{ type: 'tag', tag: 'atLeastOneTestTag' }],
|
|
254
|
-
|
|
192
|
+
services: mockServices,
|
|
193
|
+
wire: {},
|
|
255
194
|
data: {},
|
|
256
|
-
session: mockSession,
|
|
257
195
|
})
|
|
258
196
|
})
|
|
259
197
|
|
|
@@ -264,11 +202,11 @@ describe('runPermissions', () => {
|
|
|
264
202
|
addPermission('allFailTestTag', [failingPermission1, failingPermission2])
|
|
265
203
|
|
|
266
204
|
await assert.rejects(
|
|
267
|
-
runPermissions(
|
|
205
|
+
runPermissions('rpc', Math.random().toString(), {
|
|
268
206
|
wireInheritedPermissions: [{ type: 'tag', tag: 'allFailTestTag' }],
|
|
269
|
-
|
|
207
|
+
services: mockServices,
|
|
208
|
+
wire: {},
|
|
270
209
|
data: {},
|
|
271
|
-
session: mockSession,
|
|
272
210
|
}),
|
|
273
211
|
{
|
|
274
212
|
message: 'Permission denied',
|
|
@@ -282,11 +220,11 @@ describe('runPermissions', () => {
|
|
|
282
220
|
}
|
|
283
221
|
|
|
284
222
|
await assert.rejects(
|
|
285
|
-
runPermissions(
|
|
223
|
+
runPermissions('rpc', Math.random().toString(), {
|
|
286
224
|
wirePermissions,
|
|
287
|
-
|
|
225
|
+
services: mockServices,
|
|
226
|
+
wire: {},
|
|
288
227
|
data: {},
|
|
289
|
-
session: mockSession,
|
|
290
228
|
}),
|
|
291
229
|
{
|
|
292
230
|
message: 'Permission denied',
|
|
@@ -300,11 +238,11 @@ describe('runPermissions', () => {
|
|
|
300
238
|
addPermission('funcTag', [failingPermission])
|
|
301
239
|
|
|
302
240
|
await assert.rejects(
|
|
303
|
-
runPermissions(
|
|
241
|
+
runPermissions('rpc', Math.random().toString(), {
|
|
304
242
|
funcInheritedPermissions: [{ type: 'tag', tag: 'funcTag' }],
|
|
305
|
-
|
|
243
|
+
services: mockServices,
|
|
244
|
+
wire: {},
|
|
306
245
|
data: {},
|
|
307
|
-
session: mockSession,
|
|
308
246
|
}),
|
|
309
247
|
{
|
|
310
248
|
message: 'Permission denied',
|
|
@@ -318,11 +256,11 @@ describe('runPermissions', () => {
|
|
|
318
256
|
}
|
|
319
257
|
|
|
320
258
|
await assert.rejects(
|
|
321
|
-
runPermissions(
|
|
259
|
+
runPermissions('rpc', Math.random().toString(), {
|
|
322
260
|
funcPermissions,
|
|
323
|
-
|
|
261
|
+
services: mockServices,
|
|
262
|
+
wire: {},
|
|
324
263
|
data: {},
|
|
325
|
-
session: mockSession,
|
|
326
264
|
}),
|
|
327
265
|
{
|
|
328
266
|
message: 'Permission denied',
|
|
@@ -350,12 +288,12 @@ describe('runPermissions', () => {
|
|
|
350
288
|
}
|
|
351
289
|
|
|
352
290
|
// Should NOT throw because at least one permission (wirePermissions) passes
|
|
353
|
-
await runPermissions(
|
|
291
|
+
await runPermissions('rpc', Math.random().toString(), {
|
|
354
292
|
wireInheritedPermissions: [{ type: 'tag', tag: 'failingWiringTag' }],
|
|
355
293
|
wirePermissions,
|
|
356
|
-
|
|
294
|
+
services: mockServices,
|
|
295
|
+
wire: {},
|
|
357
296
|
data: {},
|
|
358
|
-
session: mockSession,
|
|
359
297
|
})
|
|
360
298
|
|
|
361
299
|
// Both permissions should be evaluated
|
|
@@ -368,11 +306,11 @@ describe('runPermissions', () => {
|
|
|
368
306
|
addPermission('arrayTestTag', arrayPermission)
|
|
369
307
|
|
|
370
308
|
// Should not throw because verifyPermissions handles array properly
|
|
371
|
-
await runPermissions(
|
|
309
|
+
await runPermissions('rpc', Math.random().toString(), {
|
|
372
310
|
wireInheritedPermissions: [{ type: 'tag', tag: 'arrayTestTag' }],
|
|
373
|
-
|
|
311
|
+
services: mockServices,
|
|
312
|
+
wire: {},
|
|
374
313
|
data: {},
|
|
375
|
-
session: mockSession,
|
|
376
314
|
})
|
|
377
315
|
})
|
|
378
316
|
|
|
@@ -384,23 +322,23 @@ describe('runPermissions', () => {
|
|
|
384
322
|
addPermission('objectTestTag', permissionGroup)
|
|
385
323
|
|
|
386
324
|
// Should not throw because verifyPermissions handles objects properly
|
|
387
|
-
await runPermissions(
|
|
325
|
+
await runPermissions('rpc', Math.random().toString(), {
|
|
388
326
|
wireInheritedPermissions: [{ type: 'tag', tag: 'objectTestTag' }],
|
|
389
|
-
|
|
327
|
+
services: mockServices,
|
|
328
|
+
wire: {},
|
|
390
329
|
data: {},
|
|
391
|
-
session: mockSession,
|
|
392
330
|
})
|
|
393
331
|
})
|
|
394
332
|
|
|
395
333
|
test('should pass correct parameters to permission functions', async () => {
|
|
396
334
|
let receivedServices: any
|
|
335
|
+
let receivedWire: any
|
|
397
336
|
let receivedData: any
|
|
398
|
-
let receivedSession: any
|
|
399
337
|
|
|
400
|
-
const testPermission = async (services: any, data: any,
|
|
338
|
+
const testPermission = async (services: any, data: any, wire: any) => {
|
|
401
339
|
receivedServices = services
|
|
402
340
|
receivedData = data
|
|
403
|
-
|
|
341
|
+
receivedWire = wire
|
|
404
342
|
return true
|
|
405
343
|
}
|
|
406
344
|
|
|
@@ -408,15 +346,15 @@ describe('runPermissions', () => {
|
|
|
408
346
|
|
|
409
347
|
addPermission('paramTestTag', [testPermission])
|
|
410
348
|
|
|
411
|
-
await runPermissions(
|
|
349
|
+
await runPermissions('rpc', Math.random().toString(), {
|
|
412
350
|
wireInheritedPermissions: [{ type: 'tag', tag: 'paramTestTag' }],
|
|
413
|
-
|
|
351
|
+
services: mockServices,
|
|
352
|
+
wire: {},
|
|
414
353
|
data: testData,
|
|
415
|
-
session: mockSession,
|
|
416
354
|
})
|
|
417
355
|
|
|
418
356
|
assert.equal(receivedServices, mockServices)
|
|
419
357
|
assert.equal(receivedData, testData)
|
|
420
|
-
assert.
|
|
358
|
+
assert.deepEqual(receivedWire, {})
|
|
421
359
|
})
|
|
422
360
|
})
|
package/src/permissions.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CoreServices,
|
|
3
|
-
CoreUserSession,
|
|
4
3
|
PikkuWiringTypes,
|
|
5
4
|
PermissionMetadata,
|
|
5
|
+
PikkuWire,
|
|
6
6
|
} from './types/core.types.js'
|
|
7
7
|
import {
|
|
8
8
|
CorePermissionGroup,
|
|
@@ -16,14 +16,14 @@ import { freezeDedupe } from './utils.js'
|
|
|
16
16
|
* This function validates permissions by iterating over permission groups and executing the corresponding permission functions. If all functions in at least one group return true, the permission is considered valid.
|
|
17
17
|
* @param services - The core services required for permission validation.
|
|
18
18
|
* @param data - The data to be used in the permission validation functions.
|
|
19
|
-
* @param
|
|
19
|
+
* @param wire - The wire object containing request/response context and session.
|
|
20
20
|
* @returns A promise that resolves to void.
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
const verifyPermissions = async <Out = any>(
|
|
23
23
|
permissions: CorePermissionGroup,
|
|
24
24
|
services: CoreServices,
|
|
25
25
|
data: any,
|
|
26
|
-
|
|
26
|
+
wire: PikkuWire<any, never, any, never, never, never>
|
|
27
27
|
): Promise<boolean> => {
|
|
28
28
|
if (!permissions) {
|
|
29
29
|
return true
|
|
@@ -38,13 +38,13 @@ export const verifyPermissions = async (
|
|
|
38
38
|
for (const funcs of permissionGroups) {
|
|
39
39
|
if (funcs instanceof Array) {
|
|
40
40
|
const permissioned = await Promise.all(
|
|
41
|
-
funcs.map((func) => func(services, data,
|
|
41
|
+
funcs.map((func) => func(services, data, wire))
|
|
42
42
|
)
|
|
43
43
|
if (permissioned.every((result) => result)) {
|
|
44
44
|
valid = true
|
|
45
45
|
}
|
|
46
46
|
} else {
|
|
47
|
-
valid = await funcs(services, data,
|
|
47
|
+
valid = await funcs(services, data, wire as any)
|
|
48
48
|
}
|
|
49
49
|
if (valid) {
|
|
50
50
|
return true
|
|
@@ -53,31 +53,6 @@ export const verifyPermissions = async (
|
|
|
53
53
|
return false
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
/**
|
|
57
|
-
* Registers a single permission function by name in the global permission store.
|
|
58
|
-
*
|
|
59
|
-
* This function is used by CLI-generated code to register permission functions
|
|
60
|
-
* that can be referenced by name in metadata. It stores permissions in a special
|
|
61
|
-
* namespace to avoid conflicts with tag-based permissions.
|
|
62
|
-
*
|
|
63
|
-
* @param {string} name - The unique name (pikkuFuncName) of the permission function.
|
|
64
|
-
* @param {CorePikkuPermission} permission - The permission function to register.
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* ```typescript
|
|
68
|
-
* // Called by CLI-generated pikku-permissions.gen.ts
|
|
69
|
-
* registerPermission('adminPermission_src_permissions_ts_10_5', adminPermission)
|
|
70
|
-
* registerPermission('readPermission_src_permissions_ts_20_10', readPermission)
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
export const registerPermission = (
|
|
74
|
-
name: string,
|
|
75
|
-
permission: CorePikkuPermission<any>
|
|
76
|
-
) => {
|
|
77
|
-
const permissionStore = pikkuState('misc', 'permissions')
|
|
78
|
-
permissionStore[name] = [permission]
|
|
79
|
-
}
|
|
80
|
-
|
|
81
56
|
/**
|
|
82
57
|
* Retrieves a registered permission function by its name.
|
|
83
58
|
*
|
|
@@ -89,10 +64,8 @@ export const registerPermission = (
|
|
|
89
64
|
*
|
|
90
65
|
* @internal
|
|
91
66
|
*/
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
): CorePikkuPermission | undefined => {
|
|
95
|
-
const permissionStore = pikkuState('misc', 'permissions')
|
|
67
|
+
const getPermissionByName = (name: string): CorePikkuPermission | undefined => {
|
|
68
|
+
const permissionStore = pikkuState(null, 'misc', 'permissions')
|
|
96
69
|
const permission = permissionStore[name]
|
|
97
70
|
if (Array.isArray(permission) && permission.length === 1) {
|
|
98
71
|
return permission[0]
|
|
@@ -130,9 +103,10 @@ export const getPermissionByName = (
|
|
|
130
103
|
*/
|
|
131
104
|
export const addPermission = (
|
|
132
105
|
tag: string,
|
|
133
|
-
permissions: CorePermissionGroup | CorePikkuPermission[]
|
|
106
|
+
permissions: CorePermissionGroup | CorePikkuPermission[],
|
|
107
|
+
packageName: string | null = null
|
|
134
108
|
): CorePermissionGroup | CorePikkuPermission[] => {
|
|
135
|
-
const tagGroups = pikkuState('permissions', 'tagGroup')
|
|
109
|
+
const tagGroups = pikkuState(packageName, 'permissions', 'tagGroup')
|
|
136
110
|
if (tagGroups[tag]) {
|
|
137
111
|
throw new Error(
|
|
138
112
|
`Permissions for tag '${tag}' already exist. Use a different tag or remove the existing permissions first.`
|
|
@@ -142,62 +116,18 @@ export const addPermission = (
|
|
|
142
116
|
return permissions
|
|
143
117
|
}
|
|
144
118
|
|
|
145
|
-
const EMPTY: readonly (CorePermissionGroup | CorePikkuPermission)[] = []
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Retrieves permissions for a given set of tags.
|
|
149
|
-
*
|
|
150
|
-
* This function looks up all permissions registered for any of the provided tags
|
|
151
|
-
* and returns them as a flattened array.
|
|
152
|
-
*
|
|
153
|
-
* @param {string[]} tags - Array of tags to look up permissions for.
|
|
154
|
-
* @returns {any[]} Array of permission functions that apply to the given tags.
|
|
155
|
-
*
|
|
156
|
-
* @example
|
|
157
|
-
* ```typescript
|
|
158
|
-
* // Get all permissions for tags 'api' and 'auth'
|
|
159
|
-
* const permissions = getPermissionsForTags(['api', 'auth'])
|
|
160
|
-
* ```
|
|
161
|
-
*/
|
|
162
|
-
export const getPermissionsForTags = (
|
|
163
|
-
tags?: string[]
|
|
164
|
-
): readonly (CorePermissionGroup | CorePikkuPermission)[] => {
|
|
165
|
-
if (!tags || tags.length === 0) {
|
|
166
|
-
return EMPTY
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const permissionsStore = pikkuState('permissions', 'tagGroup')
|
|
170
|
-
const applicablePermissions: Array<
|
|
171
|
-
CorePermissionGroup | CorePikkuPermission
|
|
172
|
-
> = []
|
|
173
|
-
|
|
174
|
-
// Collect permissions for all matching tags
|
|
175
|
-
for (const tag of new Set(tags)) {
|
|
176
|
-
const tagPermissions = permissionsStore[tag]
|
|
177
|
-
if (tagPermissions) {
|
|
178
|
-
if (Array.isArray(tagPermissions)) {
|
|
179
|
-
applicablePermissions.push(...tagPermissions)
|
|
180
|
-
} else {
|
|
181
|
-
applicablePermissions.push(tagPermissions)
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return applicablePermissions
|
|
187
|
-
}
|
|
188
|
-
|
|
189
119
|
const combinedPermissionsCache: Record<
|
|
190
120
|
PikkuWiringTypes,
|
|
191
121
|
Record<string, readonly (CorePermissionGroup | CorePikkuPermission)[]>
|
|
192
122
|
> = {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
123
|
+
http: {},
|
|
124
|
+
rpc: {},
|
|
125
|
+
channel: {},
|
|
126
|
+
queue: {},
|
|
127
|
+
scheduler: {},
|
|
128
|
+
mcp: {},
|
|
129
|
+
cli: {},
|
|
130
|
+
workflow: {},
|
|
201
131
|
}
|
|
202
132
|
|
|
203
133
|
/**
|
|
@@ -224,7 +154,7 @@ const combinedPermissionsCache: Record<
|
|
|
224
154
|
* })
|
|
225
155
|
* ```
|
|
226
156
|
*/
|
|
227
|
-
|
|
157
|
+
const combinePermissions = (
|
|
228
158
|
wireType: PikkuWiringTypes,
|
|
229
159
|
uid: string,
|
|
230
160
|
{
|
|
@@ -232,11 +162,13 @@ export const combinePermissions = (
|
|
|
232
162
|
wirePermissions,
|
|
233
163
|
funcInheritedPermissions,
|
|
234
164
|
funcPermissions,
|
|
165
|
+
packageName = null,
|
|
235
166
|
}: {
|
|
236
167
|
wireInheritedPermissions?: PermissionMetadata[]
|
|
237
168
|
wirePermissions?: CorePermissionGroup | CorePikkuPermission[]
|
|
238
169
|
funcInheritedPermissions?: PermissionMetadata[]
|
|
239
170
|
funcPermissions?: CorePermissionGroup | CorePikkuPermission[]
|
|
171
|
+
packageName?: string | null
|
|
240
172
|
} = {}
|
|
241
173
|
): readonly (CorePermissionGroup | CorePikkuPermission)[] => {
|
|
242
174
|
if (combinedPermissionsCache[wireType][uid]) {
|
|
@@ -250,7 +182,9 @@ export const combinePermissions = (
|
|
|
250
182
|
for (const meta of wireInheritedPermissions) {
|
|
251
183
|
if (meta.type === 'http') {
|
|
252
184
|
// Look up HTTP permission group from pikkuState
|
|
253
|
-
const group = pikkuState('permissions', 'httpGroup')[
|
|
185
|
+
const group = pikkuState(packageName, 'permissions', 'httpGroup')[
|
|
186
|
+
meta.route
|
|
187
|
+
]
|
|
254
188
|
if (group) {
|
|
255
189
|
if (Array.isArray(group)) {
|
|
256
190
|
resolved.push(...group)
|
|
@@ -260,7 +194,9 @@ export const combinePermissions = (
|
|
|
260
194
|
}
|
|
261
195
|
} else if (meta.type === 'tag') {
|
|
262
196
|
// Look up tag permission group from pikkuState
|
|
263
|
-
const group = pikkuState('permissions', 'tagGroup')[
|
|
197
|
+
const group = pikkuState(packageName, 'permissions', 'tagGroup')[
|
|
198
|
+
meta.tag
|
|
199
|
+
]
|
|
264
200
|
if (group) {
|
|
265
201
|
if (Array.isArray(group)) {
|
|
266
202
|
resolved.push(...group)
|
|
@@ -292,7 +228,9 @@ export const combinePermissions = (
|
|
|
292
228
|
for (const meta of funcInheritedPermissions) {
|
|
293
229
|
if (meta.type === 'tag') {
|
|
294
230
|
// Look up tag permission group from pikkuState
|
|
295
|
-
const group = pikkuState('permissions', 'tagGroup')[
|
|
231
|
+
const group = pikkuState(packageName, 'permissions', 'tagGroup')[
|
|
232
|
+
meta.tag
|
|
233
|
+
]
|
|
296
234
|
if (group) {
|
|
297
235
|
if (Array.isArray(group)) {
|
|
298
236
|
resolved.push(...group)
|
|
@@ -335,17 +273,19 @@ export const runPermissions = async (
|
|
|
335
273
|
wirePermissions,
|
|
336
274
|
funcInheritedPermissions,
|
|
337
275
|
funcPermissions,
|
|
338
|
-
|
|
276
|
+
services,
|
|
277
|
+
wire,
|
|
339
278
|
data,
|
|
340
|
-
|
|
279
|
+
packageName = null,
|
|
341
280
|
}: {
|
|
342
281
|
wireInheritedPermissions?: PermissionMetadata[]
|
|
343
282
|
wirePermissions?: CorePermissionGroup | CorePikkuPermission[]
|
|
344
283
|
funcInheritedPermissions?: PermissionMetadata[]
|
|
345
284
|
funcPermissions?: CorePermissionGroup | CorePikkuPermission[]
|
|
346
|
-
|
|
285
|
+
services: CoreServices
|
|
286
|
+
wire: PikkuWire<any, never, any, never, never, never>
|
|
347
287
|
data: any
|
|
348
|
-
|
|
288
|
+
packageName?: string | null
|
|
349
289
|
}
|
|
350
290
|
) => {
|
|
351
291
|
// Combine all permissions: wireInheritedPermissions → wirePermissions → funcInheritedPermissions → funcPermissions
|
|
@@ -354,6 +294,7 @@ export const runPermissions = async (
|
|
|
354
294
|
wirePermissions,
|
|
355
295
|
funcInheritedPermissions,
|
|
356
296
|
funcPermissions,
|
|
297
|
+
packageName,
|
|
357
298
|
})
|
|
358
299
|
|
|
359
300
|
// Check all combined permissions - at least one must pass if any exist
|
|
@@ -362,9 +303,9 @@ export const runPermissions = async (
|
|
|
362
303
|
for (const permission of allPermissions) {
|
|
363
304
|
const result = await verifyPermissions(
|
|
364
305
|
typeof permission === 'function' ? { permission } : permission,
|
|
365
|
-
|
|
306
|
+
services,
|
|
366
307
|
data,
|
|
367
|
-
|
|
308
|
+
wire
|
|
368
309
|
)
|
|
369
310
|
if (result) {
|
|
370
311
|
permissioned = true
|
|
@@ -372,7 +313,7 @@ export const runPermissions = async (
|
|
|
372
313
|
}
|
|
373
314
|
}
|
|
374
315
|
if (!permissioned) {
|
|
375
|
-
|
|
316
|
+
services.logger.debug('Permission denied - combined permissions')
|
|
376
317
|
throw new ForbiddenError('Permission denied')
|
|
377
318
|
}
|
|
378
319
|
}
|