@objectstack/plugin-reports 17.0.0-rc.5 → 17.0.0-rc.6
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 +408 -0
- package/dist/index.d.mts +34 -11
- package/dist/index.d.ts +34 -11
- package/dist/index.js +50 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +50 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,413 @@
|
|
|
1
1
|
# @objectstack/plugin-reports
|
|
2
2
|
|
|
3
|
+
## 17.0.0-rc.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f40c5b4: refactor(plugin-approvals,plugin-reports): enforcement implementations annotate the full `ExecutionContext` (#7135)
|
|
8
|
+
|
|
9
|
+
The services half of #7070, mirroring what PR #7140 did for
|
|
10
|
+
`plugin-sharing` / `plugin-audit`. #6523 converged 36 contract signatures onto
|
|
11
|
+
the complete `resolveAuthzContext` envelope, applying the #6206 ruling —
|
|
12
|
+
enforcement adjudicates on the whole envelope, never a per-site subset. The
|
|
13
|
+
implementations behind those contracts still annotated their own parameters
|
|
14
|
+
with the six-field shape the contracts used to name, so nothing they could
|
|
15
|
+
_read_ had widened.
|
|
16
|
+
|
|
17
|
+
`ApprovalService`, the approval flow-node provider and `ReportService` now
|
|
18
|
+
declare `ExecutionContext` on all 43 of those positions, and the casts the
|
|
19
|
+
narrow annotation forced are gone:
|
|
20
|
+
|
|
21
|
+
- `isOverrideActor()` read the derived `posture` (ADR-0095) through an
|
|
22
|
+
unchecked `(context as any)`. That gate decides whether a platform or tenant
|
|
23
|
+
admin may release a STUCK approval — one routed to an unstaffed position, the
|
|
24
|
+
only in-product recovery from a permanently locked record — so an erasure sat
|
|
25
|
+
directly on an enforcement input: a mistyped rung would have compiled and
|
|
26
|
+
silently denied every override. It is a declared read now.
|
|
27
|
+
- Both services' `SYSTEM_CTX` is typed as the envelope and passed as itself,
|
|
28
|
+
retiring the `SYSTEM_CTX as unknown as …` double casts at the three sites
|
|
29
|
+
that hand it to a contract method.
|
|
30
|
+
- The `(context as any).userId` / `.tenantId` reads in `ApprovalService` now
|
|
31
|
+
read declared fields.
|
|
32
|
+
- `OwnerContextResolver` returns the envelope, which is what a scheduled report
|
|
33
|
+
actually resolves for its owner (#2849 / #2980).
|
|
34
|
+
|
|
35
|
+
**No runtime behaviour changes.** The values were always complete — this
|
|
36
|
+
family's damage was type-side — so every gate answers exactly what it answered
|
|
37
|
+
before. Method parameters only WIDEN what they accept, so no caller is
|
|
38
|
+
affected, and no public export changes shape.
|
|
39
|
+
|
|
40
|
+
Casts deliberately kept, and now documented where they sit: `organizationId`
|
|
41
|
+
is not a field of the envelope at all — that spelling has its own history
|
|
42
|
+
(#5858 / `check:org-identifier`) and was held out of this change by #7070. In
|
|
43
|
+
`approval-node.ts` the single remaining assertion exists only because the
|
|
44
|
+
literal names that key; it was reduced from `as unknown as …` to a single
|
|
45
|
+
`as ExecutionContext`, which still requires the literal to be comparable to
|
|
46
|
+
the envelope.
|
|
47
|
+
|
|
48
|
+
Because a re-narrowed annotation would compile, ship and pass every test in
|
|
49
|
+
these packages, the convergence is pinned by a new compile-time module per
|
|
50
|
+
package, `exec-context-annotation.pin.ts`: it hands each parameter a fresh
|
|
51
|
+
literal naming envelope-only fields (`posture`, `accessible_org_ids`,
|
|
52
|
+
`org_user_ids`), which TypeScript's excess-property check rejects the moment a
|
|
53
|
+
parameter narrows back, plus negative cases so a parameter erased to `any`
|
|
54
|
+
cannot pass either.
|
|
55
|
+
|
|
56
|
+
The exported `SharingExecutionContext` type itself is NOT removed here: it is
|
|
57
|
+
defined in `packages/spec`, which is single-owner, so its retirement is a
|
|
58
|
+
separate follow-up.
|
|
59
|
+
|
|
60
|
+
- d0d5205: refactor(core,plugin-audit,service-storage,plugin-reports): give the `__` operation-private-key convention a single owner (#7284)
|
|
61
|
+
|
|
62
|
+
`withoutOperationPrivateKeys` — the rule that a consumer forwarding a caller's
|
|
63
|
+
execution envelope to a question about a DIFFERENT object must first drop the
|
|
64
|
+
`__`-prefixed keys plugin-security stamped for the operation in flight — had been
|
|
65
|
+
hand-copied into three packages: `plugin-audit`'s comment access hooks (#7141),
|
|
66
|
+
`service-storage`'s attachment access hooks (#7145) and `plugin-reports`' report
|
|
67
|
+
service (#7204). Each carried its own `OPERATION_PRIVATE_KEY_PREFIX` and its own
|
|
68
|
+
doc block, and the prose had already diverged while the code still agreed — the
|
|
69
|
+
shape that makes a later divergence in behaviour hard to notice.
|
|
70
|
+
|
|
71
|
+
The helper now lives once, in `@objectstack/core`
|
|
72
|
+
(`security/operation-private-keys.ts`), exported from the package root. Core is
|
|
73
|
+
the only candidate all three consumers already depend on: `plugin-security` is
|
|
74
|
+
the producer of the convention and the most honest owner, but none of the three
|
|
75
|
+
depends on it and a string-prefix filter does not justify three new dependency
|
|
76
|
+
edges onto a plugin; `@objectstack/spec` is fenced off by Prime Directive #2. The
|
|
77
|
+
new home sits beside `assemble-execution-context.ts`, which owns the other end of
|
|
78
|
+
the same lifecycle — that file is where an `ExecutionContext` is built at a
|
|
79
|
+
transport entry point, this one is where it is stripped back down before being
|
|
80
|
+
forwarded.
|
|
81
|
+
|
|
82
|
+
The full reasoning moved with the code rather than being thinned: which keys the
|
|
83
|
+
middleware stamps and why each is a widening input, why they are dropped by
|
|
84
|
+
PREFIX and never by a name list, and why the fresh copy is load-bearing in both
|
|
85
|
+
directions. Each consumer keeps only its own local half — which object _its_
|
|
86
|
+
gates actually ask about — and points at the shared home.
|
|
87
|
+
|
|
88
|
+
No behaviour change: the three copies were byte-equivalent, and all three
|
|
89
|
+
packages' suites pass unchanged. Two new pins at the home cover it — the rule's
|
|
90
|
+
own behaviour, which no package-level test had ever asserted directly, and a
|
|
91
|
+
repository-shape pin that turns red if a fourth file declares its own copy.
|
|
92
|
+
|
|
93
|
+
- cb466aa: Reports read with the caller's whole execution envelope, so a `group`-posture report no longer under-reports
|
|
94
|
+
|
|
95
|
+
`executeReport` rebuilt a five-field projection of the caller's `ExecutionContext`
|
|
96
|
+
(`userId` / `tenantId` / `positions` / `permissions` / `isSystem`) before handing it to
|
|
97
|
+
the engine read that produces the report — while the method's own comment promised
|
|
98
|
+
"reports execute with the caller's identity".
|
|
99
|
+
|
|
100
|
+
**Before.** `accessible_org_ids` was not in that projection, and the engine reads it by
|
|
101
|
+
name (`buildDriverOptions`, ADR-0105 D2 / #3623) to widen the driver's native tenant
|
|
102
|
+
scope to the caller's whole membership set under the `group` tenancy posture. Absent, the
|
|
103
|
+
drivers fall back to active-org equality — "fail toward isolation". So the identical query
|
|
104
|
+
returned the membership union in an interactive list view and collapsed to the active org
|
|
105
|
+
inside a **saved or scheduled** report: silently short rows, no error, nothing in the
|
|
106
|
+
output saying so. Measured end-to-end on a real kernel + SQL driver: three rows across two
|
|
107
|
+
member orgs came back as three interactively and two in the report, and a scheduled CSV
|
|
108
|
+
digest emailed the owner the same two. `timezone` went the same way, so a read-time
|
|
109
|
+
formula field resolved its calendar day in UTC instead of the caller's business timezone;
|
|
110
|
+
`posture`, `org_user_ids`, `systemPermissions` and `onBehalfOf` were dropped too.
|
|
111
|
+
|
|
112
|
+
**After.** The read receives the caller's envelope whole (the #6206 ruling — enforcement
|
|
113
|
+
adjudicates on the whole `resolveAuthzContext` envelope, never a per-site subset), minus
|
|
114
|
+
the `__`-prefixed keys plugin-security stamps for the operation in flight, and as a fresh
|
|
115
|
+
object so a callee's stamp cannot write back into the caller's request context. The same
|
|
116
|
+
shape `plugin-audit` (#7141) and `service-storage` (#7145) landed. Direction is unchanged
|
|
117
|
+
outside `group`: the `isolated` posture, a deployment with no posture provider, and a
|
|
118
|
+
`group` caller with an empty accessible set all still read at active-org equality.
|
|
119
|
+
|
|
120
|
+
- 2c2a212: fix(reports): owner-gate the saved-report schedule routes (#2980)
|
|
121
|
+
|
|
122
|
+
The report read/run/delete routes are owner-isolated (a caller may only touch a
|
|
123
|
+
report they own, denied as `REPORT_NOT_FOUND` to avoid leaking that the id
|
|
124
|
+
exists), but the two schedule routes bypassed that gate: `unscheduleReport` and
|
|
125
|
+
`listSchedules` took the caller `context` as `_context` and never consulted it,
|
|
126
|
+
querying under the system context (RLS-bypassing). Any authenticated caller
|
|
127
|
+
could therefore delete another owner's report schedule — a cross-owner
|
|
128
|
+
destructive write — or list another owner's schedules (leaking recipient
|
|
129
|
+
addresses and cron), by supplying an id.
|
|
130
|
+
|
|
131
|
+
Both now resolve the schedule's parent report and require the caller to own it,
|
|
132
|
+
mirroring the sibling routes:
|
|
133
|
+
|
|
134
|
+
- **`unscheduleReport`** loads the schedule, then its report, and deletes only
|
|
135
|
+
when `canAccessReport` holds; a cross-owner attempt throws `REPORT_NOT_FOUND`
|
|
136
|
+
(mapped to `404` by the REST layer, deny-as-404 anti-enumeration), while a
|
|
137
|
+
genuinely-absent schedule stays idempotent. `scheduleReport` (create) was
|
|
138
|
+
already gated via `getReport`, so only the delete/list doors were open.
|
|
139
|
+
- **`listSchedules`** returns an empty list to any non-system caller who cannot
|
|
140
|
+
access the report it is scoped to — the same non-leaking posture as
|
|
141
|
+
`listReports`. The scheduler's system context still sees every schedule.
|
|
142
|
+
|
|
143
|
+
No authoring-surface or metadata change; existing owner-path behavior is
|
|
144
|
+
unchanged.
|
|
145
|
+
|
|
146
|
+
- dadd1ad: refactor(spec,plugin-sharing): retire the exported `SharingExecutionContext` type (#7218)
|
|
147
|
+
|
|
148
|
+
<!-- adr-0087: registered sharing-execution-context-retired -->
|
|
149
|
+
|
|
150
|
+
**BREAKING — public surface removal.** `SharingExecutionContext` is deleted from
|
|
151
|
+
`@objectstack/spec` (`contracts/sharing-service`) and from
|
|
152
|
+
`@objectstack/plugin-sharing`, which re-exported it. Both `api-surface/` and
|
|
153
|
+
`export-origins/` snapshots are regenerated accordingly.
|
|
154
|
+
|
|
155
|
+
This is the deferred deletion recorded when #7070 split the convergence in two.
|
|
156
|
+
#6523 / PR #7068 converged 36 contract signatures onto the full
|
|
157
|
+
`resolveAuthzContext` envelope (`ExecutionContext`), applying the #6206 ruling —
|
|
158
|
+
enforcement adjudicates on the whole envelope, never a per-site subset. The
|
|
159
|
+
consumer halves then re-annotated the implementations: PR #7140 (identity:
|
|
160
|
+
`plugin-sharing`, `plugin-audit`) and PR #7206 (services: `plugin-approvals`,
|
|
161
|
+
`plugin-reports`). Both landed with the type still exported, because it is
|
|
162
|
+
DEFINED in `packages/spec` and that package's retirement is the spec seat's to
|
|
163
|
+
make. Nothing declares it any more, so it goes.
|
|
164
|
+
|
|
165
|
+
**Migration.** Anyone who imported `SharingExecutionContext` from either package
|
|
166
|
+
should import `ExecutionContext` from `@objectstack/spec` instead — the type the
|
|
167
|
+
contracts have declared since #7068. The old shape was six optional fields, all
|
|
168
|
+
of which exist on the envelope with the same names and types, so a value that
|
|
169
|
+
satisfied the retired type already satisfies `ExecutionContext`; only the
|
|
170
|
+
spelling of the annotation changes.
|
|
171
|
+
|
|
172
|
+
**No runtime behaviour changes.** The type was erased at compile time and no
|
|
173
|
+
signature's accepted shape moved: the contracts already took the wide envelope.
|
|
174
|
+
|
|
175
|
+
**What the retirement did NOT remove — the reason to read the pins.** Deleting
|
|
176
|
+
the type does not make re-narrowing a compile error. Structural subtyping still
|
|
177
|
+
accepts a six-field context where the envelope is expected, so the boundary is
|
|
178
|
+
held by the declared parameter type plus the pins, exactly as before. The three
|
|
179
|
+
`exec-context-annotation.pin.ts` files (`plugin-sharing`, `plugin-approvals`,
|
|
180
|
+
`plugin-reports`) told their failure story as "the parameter narrows back to
|
|
181
|
+
`SharingExecutionContext`", which a deletion would have quietly hollowed out.
|
|
182
|
+
Each now keeps the retired six-field shape as a local, non-exported SPECIMEN
|
|
183
|
+
type and refutes every enforcement parameter against it by type identity, so a
|
|
184
|
+
re-narrowing under ANY name is red — alongside the fresh-literal
|
|
185
|
+
excess-property checks they already carried. `sharing-service.test.ts` in
|
|
186
|
+
`packages/spec` is re-anchored the same way, and its "twin unchanged in shape"
|
|
187
|
+
case becomes a "twin stays retired" case. The narrative the retired type's doc
|
|
188
|
+
block carried (the measured `(context as any).posture` specimen, and why tsc
|
|
189
|
+
cannot police this) moves to the module doc of `contracts/sharing-service`,
|
|
190
|
+
which the contracts and pins now point at.
|
|
191
|
+
|
|
192
|
+
- 2465133: fix(plugins): sweep the service-lookup erasures out of the plugin composition roots, and fix the two alias-only HTTP reads it exposed (#4251 B5)
|
|
193
|
+
|
|
194
|
+
Batch B5 of the #4251 sweep: the seven remaining `packages/plugins/*` composition
|
|
195
|
+
roots. 35 lookup sites that had been erased to `any` now carry the slot's
|
|
196
|
+
contract, so the compiler checks what each plugin actually calls on the service
|
|
197
|
+
it resolved. The ratchet drops 143 sites / 32 files to 108 / 25.
|
|
198
|
+
|
|
199
|
+
**Two real defects, both of the shape this sweep exists to find.** Approvals'
|
|
200
|
+
actionable-link pages (ADR-0043) and sharing's public share-link REST routes each
|
|
201
|
+
read the HTTP server under `http-server` _only_ — the deprecated alias. The
|
|
202
|
+
ledger records `http.server` as canonical and as the only name present on every
|
|
203
|
+
provider path: `runtime.ts`'s `config.server` path registers no alias at all. On
|
|
204
|
+
that path both lookups threw, the surrounding `catch` swallowed it, and the
|
|
205
|
+
routes silently never mounted — approval e-mail action links 404'd and the
|
|
206
|
+
share-link surface was absent, with nothing in the log to say so. Both reads are
|
|
207
|
+
now canonical-first with the alias as fallback, each name in its own `try`
|
|
208
|
+
because `getService` throws on an empty slot (so `a() ?? b()` inside one `try`
|
|
209
|
+
never reaches `b` — the same correction #4393 made in metadata and
|
|
210
|
+
cloud-connection).
|
|
211
|
+
|
|
212
|
+
Typing choices follow the batch method: pure data-plane consumers take the
|
|
213
|
+
narrow contract (`IDataEngine` in reports), consumers that bind hook or
|
|
214
|
+
middleware seams take the engine seen whole (`IObjectQLEngine` in approvals,
|
|
215
|
+
sharing and pinyin-search), and slots with no contract get a **named** local
|
|
216
|
+
surface rather than `any` — plugin-email's `MailSettingsSurface`, and the
|
|
217
|
+
surfaces the consuming packages already declared (`ApprovalMessagingSurface`,
|
|
218
|
+
`SharingSecurityProbe`, `ReportEmail`). A named surface that omits a member
|
|
219
|
+
still makes the compiler name every call site; `any` says nothing.
|
|
220
|
+
|
|
221
|
+
No behaviour change beyond the two alias reads. No contract changes.
|
|
222
|
+
|
|
223
|
+
- Updated dependencies [3d5c090]
|
|
224
|
+
- Updated dependencies [e5bd768]
|
|
225
|
+
- Updated dependencies [e027b3e]
|
|
226
|
+
- Updated dependencies [c2429b0]
|
|
227
|
+
- Updated dependencies [445a0c2]
|
|
228
|
+
- Updated dependencies [f6609e6]
|
|
229
|
+
- Updated dependencies [a70358a]
|
|
230
|
+
- Updated dependencies [97e7e3c]
|
|
231
|
+
- Updated dependencies [8828b9e]
|
|
232
|
+
- Updated dependencies [53068c1]
|
|
233
|
+
- Updated dependencies [ee58392]
|
|
234
|
+
- Updated dependencies [f16e54e]
|
|
235
|
+
- Updated dependencies [06be54e]
|
|
236
|
+
- Updated dependencies [259459d]
|
|
237
|
+
- Updated dependencies [3f7f14e]
|
|
238
|
+
- Updated dependencies [6968885]
|
|
239
|
+
- Updated dependencies [eaed61f]
|
|
240
|
+
- Updated dependencies [debe2f6]
|
|
241
|
+
- Updated dependencies [97b0798]
|
|
242
|
+
- Updated dependencies [5fa04fb]
|
|
243
|
+
- Updated dependencies [43a7a8d]
|
|
244
|
+
- Updated dependencies [73f69dc]
|
|
245
|
+
- Updated dependencies [04c56aa]
|
|
246
|
+
- Updated dependencies [b3efeb7]
|
|
247
|
+
- Updated dependencies [ddd075a]
|
|
248
|
+
- Updated dependencies [88154be]
|
|
249
|
+
- Updated dependencies [e8dc61e]
|
|
250
|
+
- Updated dependencies [2f3e793]
|
|
251
|
+
- Updated dependencies [d8e8d9c]
|
|
252
|
+
- Updated dependencies [94e749b]
|
|
253
|
+
- Updated dependencies [ea1d916]
|
|
254
|
+
- Updated dependencies [ae31a19]
|
|
255
|
+
- Updated dependencies [e0f300b]
|
|
256
|
+
- Updated dependencies [62b6a2f]
|
|
257
|
+
- Updated dependencies [5b4780b]
|
|
258
|
+
- Updated dependencies [a933452]
|
|
259
|
+
- Updated dependencies [8140915]
|
|
260
|
+
- Updated dependencies [7b48cf9]
|
|
261
|
+
- Updated dependencies [b5404f4]
|
|
262
|
+
- Updated dependencies [f764691]
|
|
263
|
+
- Updated dependencies [e120a5a]
|
|
264
|
+
- Updated dependencies [e650d67]
|
|
265
|
+
- Updated dependencies [04476e7]
|
|
266
|
+
- Updated dependencies [79228cd]
|
|
267
|
+
- Updated dependencies [b3363e9]
|
|
268
|
+
- Updated dependencies [2ef1807]
|
|
269
|
+
- Updated dependencies [d03fe25]
|
|
270
|
+
- Updated dependencies [2672f85]
|
|
271
|
+
- Updated dependencies [11066f6]
|
|
272
|
+
- Updated dependencies [916af17]
|
|
273
|
+
- Updated dependencies [84c86fb]
|
|
274
|
+
- Updated dependencies [2a2a9fb]
|
|
275
|
+
- Updated dependencies [a2e157c]
|
|
276
|
+
- Updated dependencies [95c4227]
|
|
277
|
+
- Updated dependencies [2a61116]
|
|
278
|
+
- Updated dependencies [d4df105]
|
|
279
|
+
- Updated dependencies [e2798fa]
|
|
280
|
+
- Updated dependencies [0fd8556]
|
|
281
|
+
- Updated dependencies [74155c7]
|
|
282
|
+
- Updated dependencies [6908830]
|
|
283
|
+
- Updated dependencies [8b06bba]
|
|
284
|
+
- Updated dependencies [4c54037]
|
|
285
|
+
- Updated dependencies [0f7157b]
|
|
286
|
+
- Updated dependencies [d9bef45]
|
|
287
|
+
- Updated dependencies [f549a0d]
|
|
288
|
+
- Updated dependencies [82da264]
|
|
289
|
+
- Updated dependencies [f586f1a]
|
|
290
|
+
- Updated dependencies [9b9b70f]
|
|
291
|
+
- Updated dependencies [f5a9bc2]
|
|
292
|
+
- Updated dependencies [881a3cc]
|
|
293
|
+
- Updated dependencies [ad6317b]
|
|
294
|
+
- Updated dependencies [8a88885]
|
|
295
|
+
- Updated dependencies [5f7669e]
|
|
296
|
+
- Updated dependencies [becbe53]
|
|
297
|
+
- Updated dependencies [b127c8b]
|
|
298
|
+
- Updated dependencies [a80302a]
|
|
299
|
+
- Updated dependencies [474f131]
|
|
300
|
+
- Updated dependencies [050cd82]
|
|
301
|
+
- Updated dependencies [4d552af]
|
|
302
|
+
- Updated dependencies [44d677c]
|
|
303
|
+
- Updated dependencies [c32944d]
|
|
304
|
+
- Updated dependencies [1dd780f]
|
|
305
|
+
- Updated dependencies [c8d6f6e]
|
|
306
|
+
- Updated dependencies [92a67f2]
|
|
307
|
+
- Updated dependencies [9136327]
|
|
308
|
+
- Updated dependencies [bf0ae99]
|
|
309
|
+
- Updated dependencies [cb3b6cd]
|
|
310
|
+
- Updated dependencies [73b7234]
|
|
311
|
+
- Updated dependencies [d2b97c3]
|
|
312
|
+
- Updated dependencies [59b794f]
|
|
313
|
+
- Updated dependencies [fc3a36a]
|
|
314
|
+
- Updated dependencies [69787f0]
|
|
315
|
+
- Updated dependencies [5d022a1]
|
|
316
|
+
- Updated dependencies [042b9ee]
|
|
317
|
+
- Updated dependencies [f549a0d]
|
|
318
|
+
- Updated dependencies [a36db28]
|
|
319
|
+
- Updated dependencies [3f8817a]
|
|
320
|
+
- Updated dependencies [a2443e3]
|
|
321
|
+
- Updated dependencies [e1554b1]
|
|
322
|
+
- Updated dependencies [4856789]
|
|
323
|
+
- Updated dependencies [c3f4916]
|
|
324
|
+
- Updated dependencies [33e0385]
|
|
325
|
+
- Updated dependencies [2205363]
|
|
326
|
+
- Updated dependencies [09fe58d]
|
|
327
|
+
- Updated dependencies [d0a5ceb]
|
|
328
|
+
- Updated dependencies [e18a162]
|
|
329
|
+
- Updated dependencies [d6d1a50]
|
|
330
|
+
- Updated dependencies [d127ff0]
|
|
331
|
+
- Updated dependencies [9b86cf6]
|
|
332
|
+
- Updated dependencies [8825a06]
|
|
333
|
+
- Updated dependencies [5087ac6]
|
|
334
|
+
- Updated dependencies [2d1ddf0]
|
|
335
|
+
- Updated dependencies [354b00f]
|
|
336
|
+
- Updated dependencies [3de535b]
|
|
337
|
+
- Updated dependencies [fe2e15a]
|
|
338
|
+
- Updated dependencies [c6b6bb4]
|
|
339
|
+
- Updated dependencies [59c544d]
|
|
340
|
+
- Updated dependencies [2f59da0]
|
|
341
|
+
- Updated dependencies [8ad609c]
|
|
342
|
+
- Updated dependencies [bbee302]
|
|
343
|
+
- Updated dependencies [08863dd]
|
|
344
|
+
- Updated dependencies [56664f5]
|
|
345
|
+
- Updated dependencies [31cbe90]
|
|
346
|
+
- Updated dependencies [90bbf25]
|
|
347
|
+
- Updated dependencies [eb91eba]
|
|
348
|
+
- Updated dependencies [42da73d]
|
|
349
|
+
- Updated dependencies [643b7c7]
|
|
350
|
+
- Updated dependencies [d0d5205]
|
|
351
|
+
- Updated dependencies [1a15893]
|
|
352
|
+
- Updated dependencies [b70e534]
|
|
353
|
+
- Updated dependencies [2233a85]
|
|
354
|
+
- Updated dependencies [62dd69a]
|
|
355
|
+
- Updated dependencies [e15e679]
|
|
356
|
+
- Updated dependencies [2ab1257]
|
|
357
|
+
- Updated dependencies [4cc4fb7]
|
|
358
|
+
- Updated dependencies [28d1eb7]
|
|
359
|
+
- Updated dependencies [2c26040]
|
|
360
|
+
- Updated dependencies [f758cec]
|
|
361
|
+
- Updated dependencies [78f0be8]
|
|
362
|
+
- Updated dependencies [35f7fb4]
|
|
363
|
+
- Updated dependencies [a5302c7]
|
|
364
|
+
- Updated dependencies [7084313]
|
|
365
|
+
- Updated dependencies [0e043d8]
|
|
366
|
+
- Updated dependencies [dadd1ad]
|
|
367
|
+
- Updated dependencies [2f2e63c]
|
|
368
|
+
- Updated dependencies [486d526]
|
|
369
|
+
- Updated dependencies [89d7b35]
|
|
370
|
+
- Updated dependencies [85ec26d]
|
|
371
|
+
- Updated dependencies [f6476fc]
|
|
372
|
+
- Updated dependencies [4ac12ef]
|
|
373
|
+
- Updated dependencies [b88f5e8]
|
|
374
|
+
- Updated dependencies [42cc219]
|
|
375
|
+
- Updated dependencies [d42a92f]
|
|
376
|
+
- Updated dependencies [51d74ad]
|
|
377
|
+
- Updated dependencies [d7e0b42]
|
|
378
|
+
- Updated dependencies [3510e4a]
|
|
379
|
+
- Updated dependencies [aa4b90d]
|
|
380
|
+
- Updated dependencies [54299ca]
|
|
381
|
+
- Updated dependencies [dc61def]
|
|
382
|
+
- Updated dependencies [251e888]
|
|
383
|
+
- Updated dependencies [183b4c4]
|
|
384
|
+
- Updated dependencies [2fdb36e]
|
|
385
|
+
- Updated dependencies [e787608]
|
|
386
|
+
- Updated dependencies [20526f5]
|
|
387
|
+
- Updated dependencies [c5eef1d]
|
|
388
|
+
- Updated dependencies [e0f300b]
|
|
389
|
+
- Updated dependencies [761a0ba]
|
|
390
|
+
- Updated dependencies [61282f9]
|
|
391
|
+
- Updated dependencies [be87153]
|
|
392
|
+
- Updated dependencies [60f0dd8]
|
|
393
|
+
- Updated dependencies [a87c5cd]
|
|
394
|
+
- Updated dependencies [a47f338]
|
|
395
|
+
- Updated dependencies [2598216]
|
|
396
|
+
- Updated dependencies [2c7e62d]
|
|
397
|
+
- Updated dependencies [eb7613c]
|
|
398
|
+
- Updated dependencies [ecc9110]
|
|
399
|
+
- Updated dependencies [f7bd4e2]
|
|
400
|
+
- Updated dependencies [361bd5b]
|
|
401
|
+
- Updated dependencies [1818998]
|
|
402
|
+
- Updated dependencies [09ee21c]
|
|
403
|
+
- Updated dependencies [f549a0d]
|
|
404
|
+
- Updated dependencies [3fc2e48]
|
|
405
|
+
- Updated dependencies [e8f435c]
|
|
406
|
+
- Updated dependencies [41610f6]
|
|
407
|
+
- @objectstack/spec@17.0.0-rc.6
|
|
408
|
+
- @objectstack/platform-objects@17.0.0-rc.6
|
|
409
|
+
- @objectstack/core@17.0.0-rc.6
|
|
410
|
+
|
|
3
411
|
## 17.0.0-rc.5
|
|
4
412
|
|
|
5
413
|
### Patch Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { SysReportSchedule, SysSavedReport } from '@objectstack/platform-objects/audit';
|
|
2
|
-
import { IReportService,
|
|
2
|
+
import { IReportService, SaveReportInput, SavedReport, ReportRunResult, ScheduleReportInput, ReportSchedule, ReportFormat } from '@objectstack/spec/contracts';
|
|
3
3
|
export { IReportService, ReportFormat, ReportQuery, ReportRunResult, ReportSchedule, SaveReportInput, SavedReport, ScheduleReportInput } from '@objectstack/spec/contracts';
|
|
4
|
+
import { ExecutionContext } from '@objectstack/spec/kernel';
|
|
4
5
|
import { Plugin, PluginContext } from '@objectstack/core';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -40,6 +41,26 @@ interface ReportClock {
|
|
|
40
41
|
now(): Date;
|
|
41
42
|
}
|
|
42
43
|
declare function renderReport(rows: any[], format: ReportFormat, fields?: string[]): string;
|
|
44
|
+
/**
|
|
45
|
+
* Why this module strips the operation-private keys before forwarding an
|
|
46
|
+
* envelope — the LOCAL half of the argument.
|
|
47
|
+
*
|
|
48
|
+
* A report read asks about `report.object_name`, which is not necessarily the
|
|
49
|
+
* object the caller's envelope last carried a `__`-prefixed depth for: a REST
|
|
50
|
+
* request that touched another object before reaching `/reports/:id/run` hands
|
|
51
|
+
* over an envelope plugin-security's middleware has already written into, and
|
|
52
|
+
* that middleware only OVERWRITES `__readScope` when it resolves permission sets
|
|
53
|
+
* for the new object (`if (permissionSets.length > 0)`). A stale depth therefore
|
|
54
|
+
* survives into a question it was never resolved for.
|
|
55
|
+
*
|
|
56
|
+
* [#7204] The general rule — which keys those are, why they are dropped by
|
|
57
|
+
* PREFIX rather than by a name list, and why the copy is load-bearing in both
|
|
58
|
+
* directions — is `withoutOperationPrivateKeys` in `@objectstack/core`. It was
|
|
59
|
+
* hand-copied into this file, `plugin-audit`'s comment kit and
|
|
60
|
+
* `service-storage`'s attachment kit before #7284 gave it one owner; ⛔ import
|
|
61
|
+
* it, never re-derive it locally (`operation-private-keys.pin.test.ts` catches
|
|
62
|
+
* the fourth copy).
|
|
63
|
+
*/
|
|
43
64
|
/**
|
|
44
65
|
* Resolves a saved report's owner (`owner_id`) into a real, RLS-bearing
|
|
45
66
|
* `ExecutionContext` so a **scheduled** report executes under the owner's
|
|
@@ -50,7 +71,7 @@ declare function renderReport(rows: any[], format: ReportFormat, fields?: string
|
|
|
50
71
|
* resolver is the reports-surface consumer of ADR-0073's user-less identity
|
|
51
72
|
* resolution.
|
|
52
73
|
*/
|
|
53
|
-
type OwnerContextResolver = (ownerId: string) => Promise<
|
|
74
|
+
type OwnerContextResolver = (ownerId: string) => Promise<ExecutionContext | null>;
|
|
54
75
|
interface ReportServiceOptions {
|
|
55
76
|
engine: ReportEngine;
|
|
56
77
|
email?: ReportEmail;
|
|
@@ -125,21 +146,23 @@ declare class ReportService implements IReportService {
|
|
|
125
146
|
private canAccessReport;
|
|
126
147
|
/** Raw metadata read of a saved report by id (no authz — callers gate). */
|
|
127
148
|
private loadReportRow;
|
|
128
|
-
|
|
149
|
+
/** Raw metadata read of a report schedule by id (no authz — callers gate). */
|
|
150
|
+
private loadScheduleRow;
|
|
151
|
+
saveReport(input: SaveReportInput, context: ExecutionContext): Promise<SavedReport>;
|
|
129
152
|
listReports(filter: {
|
|
130
153
|
object?: string;
|
|
131
154
|
ownerId?: string;
|
|
132
|
-
} | undefined, context:
|
|
133
|
-
getReport(reportId: string, context:
|
|
134
|
-
deleteReport(reportId: string, context:
|
|
135
|
-
run(reportId: string, context:
|
|
136
|
-
runAdHoc(input: SaveReportInput, context:
|
|
155
|
+
} | undefined, context: ExecutionContext): Promise<SavedReport[]>;
|
|
156
|
+
getReport(reportId: string, context: ExecutionContext): Promise<SavedReport | null>;
|
|
157
|
+
deleteReport(reportId: string, context: ExecutionContext): Promise<void>;
|
|
158
|
+
run(reportId: string, context: ExecutionContext): Promise<ReportRunResult>;
|
|
159
|
+
runAdHoc(input: SaveReportInput, context: ExecutionContext): Promise<ReportRunResult>;
|
|
137
160
|
private executeReport;
|
|
138
|
-
scheduleReport(input: ScheduleReportInput, context:
|
|
139
|
-
unscheduleReport(scheduleId: string,
|
|
161
|
+
scheduleReport(input: ScheduleReportInput, context: ExecutionContext): Promise<ReportSchedule>;
|
|
162
|
+
unscheduleReport(scheduleId: string, context: ExecutionContext): Promise<void>;
|
|
140
163
|
listSchedules(filter: {
|
|
141
164
|
reportId?: string;
|
|
142
|
-
} | undefined,
|
|
165
|
+
} | undefined, context: ExecutionContext): Promise<ReportSchedule[]>;
|
|
143
166
|
dispatchDue(now?: Date): Promise<{
|
|
144
167
|
fired: number;
|
|
145
168
|
failed: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { SysReportSchedule, SysSavedReport } from '@objectstack/platform-objects/audit';
|
|
2
|
-
import { IReportService,
|
|
2
|
+
import { IReportService, SaveReportInput, SavedReport, ReportRunResult, ScheduleReportInput, ReportSchedule, ReportFormat } from '@objectstack/spec/contracts';
|
|
3
3
|
export { IReportService, ReportFormat, ReportQuery, ReportRunResult, ReportSchedule, SaveReportInput, SavedReport, ScheduleReportInput } from '@objectstack/spec/contracts';
|
|
4
|
+
import { ExecutionContext } from '@objectstack/spec/kernel';
|
|
4
5
|
import { Plugin, PluginContext } from '@objectstack/core';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -40,6 +41,26 @@ interface ReportClock {
|
|
|
40
41
|
now(): Date;
|
|
41
42
|
}
|
|
42
43
|
declare function renderReport(rows: any[], format: ReportFormat, fields?: string[]): string;
|
|
44
|
+
/**
|
|
45
|
+
* Why this module strips the operation-private keys before forwarding an
|
|
46
|
+
* envelope — the LOCAL half of the argument.
|
|
47
|
+
*
|
|
48
|
+
* A report read asks about `report.object_name`, which is not necessarily the
|
|
49
|
+
* object the caller's envelope last carried a `__`-prefixed depth for: a REST
|
|
50
|
+
* request that touched another object before reaching `/reports/:id/run` hands
|
|
51
|
+
* over an envelope plugin-security's middleware has already written into, and
|
|
52
|
+
* that middleware only OVERWRITES `__readScope` when it resolves permission sets
|
|
53
|
+
* for the new object (`if (permissionSets.length > 0)`). A stale depth therefore
|
|
54
|
+
* survives into a question it was never resolved for.
|
|
55
|
+
*
|
|
56
|
+
* [#7204] The general rule — which keys those are, why they are dropped by
|
|
57
|
+
* PREFIX rather than by a name list, and why the copy is load-bearing in both
|
|
58
|
+
* directions — is `withoutOperationPrivateKeys` in `@objectstack/core`. It was
|
|
59
|
+
* hand-copied into this file, `plugin-audit`'s comment kit and
|
|
60
|
+
* `service-storage`'s attachment kit before #7284 gave it one owner; ⛔ import
|
|
61
|
+
* it, never re-derive it locally (`operation-private-keys.pin.test.ts` catches
|
|
62
|
+
* the fourth copy).
|
|
63
|
+
*/
|
|
43
64
|
/**
|
|
44
65
|
* Resolves a saved report's owner (`owner_id`) into a real, RLS-bearing
|
|
45
66
|
* `ExecutionContext` so a **scheduled** report executes under the owner's
|
|
@@ -50,7 +71,7 @@ declare function renderReport(rows: any[], format: ReportFormat, fields?: string
|
|
|
50
71
|
* resolver is the reports-surface consumer of ADR-0073's user-less identity
|
|
51
72
|
* resolution.
|
|
52
73
|
*/
|
|
53
|
-
type OwnerContextResolver = (ownerId: string) => Promise<
|
|
74
|
+
type OwnerContextResolver = (ownerId: string) => Promise<ExecutionContext | null>;
|
|
54
75
|
interface ReportServiceOptions {
|
|
55
76
|
engine: ReportEngine;
|
|
56
77
|
email?: ReportEmail;
|
|
@@ -125,21 +146,23 @@ declare class ReportService implements IReportService {
|
|
|
125
146
|
private canAccessReport;
|
|
126
147
|
/** Raw metadata read of a saved report by id (no authz — callers gate). */
|
|
127
148
|
private loadReportRow;
|
|
128
|
-
|
|
149
|
+
/** Raw metadata read of a report schedule by id (no authz — callers gate). */
|
|
150
|
+
private loadScheduleRow;
|
|
151
|
+
saveReport(input: SaveReportInput, context: ExecutionContext): Promise<SavedReport>;
|
|
129
152
|
listReports(filter: {
|
|
130
153
|
object?: string;
|
|
131
154
|
ownerId?: string;
|
|
132
|
-
} | undefined, context:
|
|
133
|
-
getReport(reportId: string, context:
|
|
134
|
-
deleteReport(reportId: string, context:
|
|
135
|
-
run(reportId: string, context:
|
|
136
|
-
runAdHoc(input: SaveReportInput, context:
|
|
155
|
+
} | undefined, context: ExecutionContext): Promise<SavedReport[]>;
|
|
156
|
+
getReport(reportId: string, context: ExecutionContext): Promise<SavedReport | null>;
|
|
157
|
+
deleteReport(reportId: string, context: ExecutionContext): Promise<void>;
|
|
158
|
+
run(reportId: string, context: ExecutionContext): Promise<ReportRunResult>;
|
|
159
|
+
runAdHoc(input: SaveReportInput, context: ExecutionContext): Promise<ReportRunResult>;
|
|
137
160
|
private executeReport;
|
|
138
|
-
scheduleReport(input: ScheduleReportInput, context:
|
|
139
|
-
unscheduleReport(scheduleId: string,
|
|
161
|
+
scheduleReport(input: ScheduleReportInput, context: ExecutionContext): Promise<ReportSchedule>;
|
|
162
|
+
unscheduleReport(scheduleId: string, context: ExecutionContext): Promise<void>;
|
|
140
163
|
listSchedules(filter: {
|
|
141
164
|
reportId?: string;
|
|
142
|
-
} | undefined,
|
|
165
|
+
} | undefined, context: ExecutionContext): Promise<ReportSchedule[]>;
|
|
143
166
|
dispatchDue(now?: Date): Promise<{
|
|
144
167
|
fired: number;
|
|
145
168
|
failed: number;
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
30
30
|
var import_audit2 = require("@objectstack/platform-objects/audit");
|
|
31
31
|
|
|
32
32
|
// src/report-service.ts
|
|
33
|
+
var import_core = require("@objectstack/core");
|
|
33
34
|
var import_croner = require("croner");
|
|
34
35
|
var SYSTEM_CTX = { isSystem: true, positions: [], permissions: [] };
|
|
35
36
|
var DEFAULT_FORMAT = "csv";
|
|
@@ -219,6 +220,15 @@ var ReportService = class {
|
|
|
219
220
|
});
|
|
220
221
|
return Array.isArray(rows) && rows[0] ? rows[0] : null;
|
|
221
222
|
}
|
|
223
|
+
/** Raw metadata read of a report schedule by id (no authz — callers gate). */
|
|
224
|
+
async loadScheduleRow(scheduleId) {
|
|
225
|
+
const rows = await this.engine.find("sys_report_schedule", {
|
|
226
|
+
where: { id: scheduleId },
|
|
227
|
+
limit: 1,
|
|
228
|
+
context: SYSTEM_CTX
|
|
229
|
+
});
|
|
230
|
+
return Array.isArray(rows) && rows[0] ? rows[0] : null;
|
|
231
|
+
}
|
|
222
232
|
// ── Report CRUD ────────────────────────────────────────────────
|
|
223
233
|
async saveReport(input, context) {
|
|
224
234
|
if (!input.name) throw new Error("VALIDATION_FAILED: name is required");
|
|
@@ -326,9 +336,35 @@ var ReportService = class {
|
|
|
326
336
|
// Reports execute with the caller's identity so sharing rules
|
|
327
337
|
// (if installed) apply. Falls back to system bypass only when
|
|
328
338
|
// the report definition was created by a system writer.
|
|
339
|
+
//
|
|
340
|
+
// [#7204] The WHOLE envelope, not a rebuilt subset of it — the #6206
|
|
341
|
+
// ruling (#6523): a read that adjudicates on the caller's identity
|
|
342
|
+
// adjudicates on the whole `resolveAuthzContext` envelope. The
|
|
343
|
+
// five-field projection this replaced (`userId` / `tenantId` /
|
|
344
|
+
// `positions` / `permissions` / `isSystem`) was doing two jobs, and
|
|
345
|
+
// only one of them was correct:
|
|
346
|
+
//
|
|
347
|
+
// - dropping the middleware-private keys — CORRECT, and preserved
|
|
348
|
+
// above by {@link withoutOperationPrivateKeys};
|
|
349
|
+
// - dropping the PRINCIPAL fields — the defect. `accessible_org_ids`
|
|
350
|
+
// (ADR-0105 D2) is the one that changes rows: `buildDriverOptions`
|
|
351
|
+
// reads it BY NAME to widen the driver's native tenant scope to the
|
|
352
|
+
// caller's membership union under the `group` posture, and an absent
|
|
353
|
+
// set makes drivers "fall back to equality: fail toward isolation".
|
|
354
|
+
// So the same query returned the union in an interactive list view
|
|
355
|
+
// and collapsed to active-org equality inside a saved or scheduled
|
|
356
|
+
// report — silently short rows, no error. `timezone` is read two
|
|
357
|
+
// lines up in the same engine method (`hasTz`) and again by
|
|
358
|
+
// `applyFormulaPlan` for read-time formula fields, and `posture`,
|
|
359
|
+
// `org_user_ids`, `systemPermissions` and `onBehalfOf` went the same
|
|
360
|
+
// way; they are forwarded now for the same reason — the envelope is
|
|
361
|
+
// the contract's unit.
|
|
362
|
+
//
|
|
363
|
+
// The three defaults below are byte-for-byte what the projection
|
|
364
|
+
// produced for an envelope that omits them, and are kept so this change
|
|
365
|
+
// adds fields without changing any that were already there.
|
|
329
366
|
context: {
|
|
330
|
-
|
|
331
|
-
tenantId: context.tenantId,
|
|
367
|
+
...(0, import_core.withoutOperationPrivateKeys)(context),
|
|
332
368
|
positions: context.positions ?? [],
|
|
333
369
|
permissions: context.permissions ?? [],
|
|
334
370
|
isSystem: context.isSystem ?? false
|
|
@@ -401,11 +437,21 @@ var ReportService = class {
|
|
|
401
437
|
await this.engine.insert("sys_report_schedule", row, { context: SYSTEM_CTX });
|
|
402
438
|
return rowFromSchedule(row);
|
|
403
439
|
}
|
|
404
|
-
async unscheduleReport(scheduleId,
|
|
440
|
+
async unscheduleReport(scheduleId, context) {
|
|
405
441
|
if (!scheduleId) throw new Error("VALIDATION_FAILED: scheduleId is required");
|
|
442
|
+
const schedule = await this.loadScheduleRow(scheduleId);
|
|
443
|
+
if (!schedule) return;
|
|
444
|
+
const report = await this.loadReportRow(schedule.report_id);
|
|
445
|
+
if (!this.canAccessReport(report, context)) {
|
|
446
|
+
throw new Error(`REPORT_NOT_FOUND: ${scheduleId}`);
|
|
447
|
+
}
|
|
406
448
|
await this.engine.delete("sys_report_schedule", { where: { id: scheduleId }, context: SYSTEM_CTX });
|
|
407
449
|
}
|
|
408
|
-
async listSchedules(filter,
|
|
450
|
+
async listSchedules(filter, context) {
|
|
451
|
+
if (!context?.isSystem) {
|
|
452
|
+
if (!filter?.reportId) return [];
|
|
453
|
+
if (!await this.getReport(filter.reportId, context)) return [];
|
|
454
|
+
}
|
|
409
455
|
const f = {};
|
|
410
456
|
if (filter?.reportId) f.report_id = filter.reportId;
|
|
411
457
|
const rows = await this.engine.find("sys_report_schedule", {
|