@7365admin1/core 3.48.1-staging.159 → 3.48.1-staging.160
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/.changeset/occurrence-book-site-scope.md +50 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +19 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/test/e2e/harness.mjs +19 -0
- package/test/e2e/occurrence-book-scope.e2e.test.mjs +368 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/core": minor
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Scope the daily occurrence book routes to the estate the book belongs to.
|
|
6
|
+
|
|
7
|
+
The daily occurrence book is one estate's security log for one day — the record
|
|
8
|
+
the guards write every shift, and the record an estate is asked to produce when
|
|
9
|
+
something goes wrong. Every route on `/api/occurrence-books` carried
|
|
10
|
+
`requireAuth` and nothing more, and `occurrence-book.controller.ts` held no
|
|
11
|
+
authorization identifier of any kind. `requireAuth` proves a session exists; it
|
|
12
|
+
makes no organisation or site decision. So being signed in anywhere on the
|
|
13
|
+
platform was enough to read another client's log, re-date it, re-open or close
|
|
14
|
+
it, or delete the day outright.
|
|
15
|
+
|
|
16
|
+
All five handlers now decide before any work is done. The route count was
|
|
17
|
+
verified at the registration site — `occurrence-book.route.ts` mounts **five**
|
|
18
|
+
routes, not the three the earlier inventory recorded.
|
|
19
|
+
|
|
20
|
+
- `POST /` and `GET /` already REQUIRE a site (in the body and the query
|
|
21
|
+
respectively), so the guard checks a site the request already names and no
|
|
22
|
+
caller's contract changes.
|
|
23
|
+
- `GET /id/:id`, `PUT /id/:id` and `DELETE /id/:id` name only a record, so the
|
|
24
|
+
site is read off the STORED row through the new `requireOccurrenceBookReach`.
|
|
25
|
+
A `site` in the caller's own body or query cannot stand in for it.
|
|
26
|
+
|
|
27
|
+
`requireOccurrenceBookReach` is built on a new raw reader,
|
|
28
|
+
`getRawOccurrenceBookById`. The existing `getOccurrenceBookById` is CACHED and
|
|
29
|
+
throws `NotFoundError`, so a reach check built on it would have decided who may
|
|
30
|
+
touch a book from a cache entry, and would have answered 404 with the wrong
|
|
31
|
+
message before the guard ever ran.
|
|
32
|
+
|
|
33
|
+
The rule is the existing one, `requireSiteReach` -> `entitleSite`, already used
|
|
34
|
+
by `/api/feedbacks`, `/api/work-orders`, `/api/manpower-monitoring`,
|
|
35
|
+
`/api/attendances`, `/api/remarks`, `/api/vehicles`, `/api/documents` and
|
|
36
|
+
`/api/incident-reports`. The `customer.sites` branch is load-bearing here: the
|
|
37
|
+
guards who WRITE this book are the contracted security agency's staff and hold
|
|
38
|
+
no membership in the estate's own organisation, so a flat organisation check
|
|
39
|
+
would lock the security app out of the log it exists to keep.
|
|
40
|
+
|
|
41
|
+
Each guard sits AFTER the handler's own Joi validation, so a malformed id still
|
|
42
|
+
answers 400 exactly as before. A row that does not exist has no site, and
|
|
43
|
+
`requireSiteReach` refuses a missing site, so an id matching nothing is refused
|
|
44
|
+
rather than falling through.
|
|
45
|
+
|
|
46
|
+
`PUT /id/:id` cannot MOVE a book — `schemaUpdateOccurrenceBook` does not accept
|
|
47
|
+
`site` at all — so there is no destination to check.
|
|
48
|
+
|
|
49
|
+
`/api/occurrence-entries` and `/api/occurrence-subjects` are separate mounts and
|
|
50
|
+
are not touched here; each gets its own change.
|
package/dist/index.d.ts
CHANGED
|
@@ -6810,6 +6810,7 @@ declare function useOccurrenceBookRepo(): {
|
|
|
6810
6810
|
pageRange: string;
|
|
6811
6811
|
} | TOccurrenceBook>;
|
|
6812
6812
|
getOccurrenceBookById: (_id: string | ObjectId, session?: ClientSession) => Promise<mongodb.WithId<bson.Document> | TOccurrenceBook>;
|
|
6813
|
+
getRawOccurrenceBookById: (_id: string | ObjectId) => Promise<mongodb.WithId<bson.Document> | null>;
|
|
6813
6814
|
updateOccurrenceBookById: (_id: ObjectId | string, value: Partial<TOccurrenceBook>, session?: ClientSession) => Promise<mongodb.UpdateResult<bson.Document>>;
|
|
6814
6815
|
deleteOccurrenceBookById: (_id: string | ObjectId) => Promise<number>;
|
|
6815
6816
|
closeOccurrenceBooks: (session?: ClientSession) => Promise<mongodb.UpdateResult<bson.Document>>;
|
|
@@ -6827,6 +6828,35 @@ declare function useOccurrenceBookService(): {
|
|
|
6827
6828
|
processCloseDOB: () => Promise<void>;
|
|
6828
6829
|
};
|
|
6829
6830
|
|
|
6831
|
+
/**
|
|
6832
|
+
* Who may see, or change, a site's daily occurrence book.
|
|
6833
|
+
*
|
|
6834
|
+
* The daily occurrence book is one estate's security log for one day - the
|
|
6835
|
+
* record the guards write every shift, and the record an estate is asked to
|
|
6836
|
+
* produce when something goes wrong. Every route on `/api/occurrence-books`
|
|
6837
|
+
* carried `requireAuth` and nothing more, and this controller held no
|
|
6838
|
+
* authorization identifier of any kind, so being signed in anywhere on the
|
|
6839
|
+
* platform was enough to read another client's log, re-date it, re-open or
|
|
6840
|
+
* close it, or delete the day outright.
|
|
6841
|
+
*
|
|
6842
|
+
* The estate is the scope. `requireSiteReach` is the rule `/api/feedbacks`,
|
|
6843
|
+
* `/api/work-orders`, `/api/manpower-monitoring`, `/api/attendances`,
|
|
6844
|
+
* `/api/remarks`, `/api/vehicles`, `/api/documents` and `/api/incident-reports`
|
|
6845
|
+
* already use, so the contracted security agency that keeps this log reaches
|
|
6846
|
+
* the estate through `customer.sites` and needs no membership in its
|
|
6847
|
+
* organisation. That branch is not incidental here - the guards who write the
|
|
6848
|
+
* book are the agency's staff, not the estate's.
|
|
6849
|
+
*
|
|
6850
|
+
* - `POST /` and `GET /` already REQUIRE a site, in the body and the query
|
|
6851
|
+
* respectively, so the guard checks the site the request already names and
|
|
6852
|
+
* no caller's contract changes.
|
|
6853
|
+
* - The three record routes name only a record, so the site is read off the
|
|
6854
|
+
* STORED row via `requireOccurrenceBookReach` and a `site` in the caller's
|
|
6855
|
+
* own body cannot stand in for it.
|
|
6856
|
+
*
|
|
6857
|
+
* `PUT /id/:id` cannot MOVE a book: `schemaUpdateOccurrenceBook` does not
|
|
6858
|
+
* accept `site` at all, so there is no destination to check.
|
|
6859
|
+
*/
|
|
6830
6860
|
declare function useOccurrenceBookController(): {
|
|
6831
6861
|
add: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
6832
6862
|
getAll: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -66160,6 +66160,14 @@ function useOccurrenceBookRepo() {
|
|
|
66160
66160
|
throw error;
|
|
66161
66161
|
}
|
|
66162
66162
|
}
|
|
66163
|
+
async function getRawOccurrenceBookById(_id) {
|
|
66164
|
+
try {
|
|
66165
|
+
_id = new import_mongodb122.ObjectId(_id);
|
|
66166
|
+
} catch (error) {
|
|
66167
|
+
throw new import_node_server_utils192.BadRequestError("Invalid occurrence book ID format.");
|
|
66168
|
+
}
|
|
66169
|
+
return await collection.findOne({ _id });
|
|
66170
|
+
}
|
|
66163
66171
|
async function updateOccurrenceBookById(_id, value, session) {
|
|
66164
66172
|
value.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
66165
66173
|
try {
|
|
@@ -66296,6 +66304,7 @@ function useOccurrenceBookRepo() {
|
|
|
66296
66304
|
add,
|
|
66297
66305
|
getAll,
|
|
66298
66306
|
getOccurrenceBookById,
|
|
66307
|
+
getRawOccurrenceBookById,
|
|
66299
66308
|
updateOccurrenceBookById,
|
|
66300
66309
|
deleteOccurrenceBookById,
|
|
66301
66310
|
closeOccurrenceBooks,
|
|
@@ -66428,8 +66437,13 @@ function useOccurrenceBookController() {
|
|
|
66428
66437
|
const {
|
|
66429
66438
|
getAll: _getAll,
|
|
66430
66439
|
getOccurrenceBookById: _getOccurrenceBookById,
|
|
66440
|
+
getRawOccurrenceBookById: _getRawOccurrenceBookById,
|
|
66431
66441
|
deleteOccurrenceBookById: _deleteOccurrenceBookById
|
|
66432
66442
|
} = useOccurrenceBookRepo();
|
|
66443
|
+
async function requireOccurrenceBookReach(req, _id) {
|
|
66444
|
+
const row = await _getRawOccurrenceBookById(_id);
|
|
66445
|
+
await requireSiteReach(req, row?.site);
|
|
66446
|
+
}
|
|
66433
66447
|
async function add(req, res, next) {
|
|
66434
66448
|
const payload = { ...req.body };
|
|
66435
66449
|
const { error } = schemaOccurrenceBook.validate(payload, {
|
|
@@ -66442,6 +66456,7 @@ function useOccurrenceBookController() {
|
|
|
66442
66456
|
return;
|
|
66443
66457
|
}
|
|
66444
66458
|
try {
|
|
66459
|
+
await requireSiteReach(req, payload.site);
|
|
66445
66460
|
const data = await _add(payload);
|
|
66446
66461
|
res.status(201).json(data);
|
|
66447
66462
|
return;
|
|
@@ -66493,6 +66508,7 @@ function useOccurrenceBookController() {
|
|
|
66493
66508
|
}
|
|
66494
66509
|
});
|
|
66495
66510
|
try {
|
|
66511
|
+
await requireSiteReach(req, site);
|
|
66496
66512
|
const data = await _getAll({
|
|
66497
66513
|
search,
|
|
66498
66514
|
page,
|
|
@@ -66520,6 +66536,7 @@ function useOccurrenceBookController() {
|
|
|
66520
66536
|
return;
|
|
66521
66537
|
}
|
|
66522
66538
|
try {
|
|
66539
|
+
await requireOccurrenceBookReach(req, _id);
|
|
66523
66540
|
const data = await _getOccurrenceBookById(_id);
|
|
66524
66541
|
res.status(200).json(data);
|
|
66525
66542
|
return;
|
|
@@ -66542,6 +66559,7 @@ function useOccurrenceBookController() {
|
|
|
66542
66559
|
return;
|
|
66543
66560
|
}
|
|
66544
66561
|
try {
|
|
66562
|
+
await requireOccurrenceBookReach(req, _id);
|
|
66545
66563
|
const result = await _updateOccurrenceBookById(_id, req.body);
|
|
66546
66564
|
res.status(200).json({ message: result });
|
|
66547
66565
|
return;
|
|
@@ -66561,6 +66579,7 @@ function useOccurrenceBookController() {
|
|
|
66561
66579
|
return;
|
|
66562
66580
|
}
|
|
66563
66581
|
try {
|
|
66582
|
+
await requireOccurrenceBookReach(req, _id);
|
|
66564
66583
|
await _deleteOccurrenceBookById(_id);
|
|
66565
66584
|
res.status(200).json({ message: "Successfully deleted occurrence book." });
|
|
66566
66585
|
return;
|