@7365admin1/core 3.48.1-staging.162 → 3.48.1-staging.163
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/nfc-patrol-log-site-scope.md +56 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/test/e2e/harness.mjs +14 -0
- package/test/e2e/nfc-patrol-log-scope.e2e.test.mjs +484 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/core": minor
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Scope the NFC patrol log routes to the estate the round was walked at, and take
|
|
6
|
+
the author from the session.
|
|
7
|
+
|
|
8
|
+
An NFC patrol log is the proof that a round was actually walked: the route, the
|
|
9
|
+
day, each checkpoint the guard tapped a tag against, the ones that were skipped
|
|
10
|
+
and why, and the signature that closes the round off. It is the document a
|
|
11
|
+
client is shown when they ask whether their estate was patrolled.
|
|
12
|
+
|
|
13
|
+
Every route on `/api/nfc-patrol-log` carried `requireAuth` and nothing more, and
|
|
14
|
+
`nfc-patrol-log.controller.ts` held no authorization identifier of any kind.
|
|
15
|
+
`requireAuth` proves a session exists; it makes no organisation or site
|
|
16
|
+
decision. So being signed in anywhere on the platform was enough to read another
|
|
17
|
+
client's patrol history, **mark a checkpoint on another estate's round as
|
|
18
|
+
completed**, or **sign that round off as done** — falsifying the patrol proof,
|
|
19
|
+
not merely reading it.
|
|
20
|
+
|
|
21
|
+
All five handlers now decide before any work is done. The route count was
|
|
22
|
+
verified at the registration site — `nfc-patrol-log.route.ts` mounts **five**
|
|
23
|
+
routes, not the four the earlier inventory recorded.
|
|
24
|
+
|
|
25
|
+
- `POST /` and `GET /` already REQUIRE a site (`schemaCreateNfcPatrolLog` and
|
|
26
|
+
the query schema both have `site` required), so the guard checks a site the
|
|
27
|
+
request already names and no caller's contract changes. The repository does
|
|
28
|
+
genuinely apply that `site` — it is the first term of the aggregation match,
|
|
29
|
+
not a parameter it accepts and ignores.
|
|
30
|
+
- `PATCH /:id/checkpoint`, `GET /:id` and `PATCH /:id/sign` name only a record,
|
|
31
|
+
so the site is read off the STORED log through `requireNfcPatrolLogReach`.
|
|
32
|
+
`getById` on this repository is NOT cached, so no separate raw reader was
|
|
33
|
+
needed here, unlike the occurrence mounts.
|
|
34
|
+
- `POST /`'s `createdBy` came from the request body and was never compared to
|
|
35
|
+
the session. It comes from `callerId(req)` now. The field is still accepted,
|
|
36
|
+
so no caller starts to 400; it is simply ignored.
|
|
37
|
+
|
|
38
|
+
None of the three record routes can MOVE a log: `updateCheckpoint` sets only the
|
|
39
|
+
checkpoint fields and `sign` sets only the signature fields, so there is no
|
|
40
|
+
destination to check.
|
|
41
|
+
|
|
42
|
+
The rule is the existing one, `requireSiteReach` -> `entitleSite`. The
|
|
43
|
+
`customer.sites` branch is load-bearing here more than on any other mount: the
|
|
44
|
+
people who walk these rounds are the contracted security agency's guards and
|
|
45
|
+
hold no membership in the estate's own organisation.
|
|
46
|
+
|
|
47
|
+
`GET /:id` previously answered **200 with `null`** for an id matching nothing,
|
|
48
|
+
because the handler passed the repository's `null` straight to `res.json`. It
|
|
49
|
+
now refuses — a log that does not exist has no site, and `requireSiteReach`
|
|
50
|
+
refuses a missing site.
|
|
51
|
+
|
|
52
|
+
Sibling mounts on the same family — `/nfc-patrol-tag`, `/nfc-patrol-settings`,
|
|
53
|
+
`/nfc-patrol-route` and `/patrol-routes` — are NOT touched here. The first three
|
|
54
|
+
carry `/public` routes behind `requireSessionOrLegacyToken`, which is inert
|
|
55
|
+
while `ENFORCE_NFC_PUBLIC_AUTH` is off, so guarding their shared handlers would
|
|
56
|
+
refuse a live legacy screen. That needs the SEC-028 decision first.
|
package/dist/index.d.ts
CHANGED
|
@@ -7916,6 +7916,40 @@ declare function useNfcPatrolLogService(): {
|
|
|
7916
7916
|
sign: (id: string, value: TSignNfcPatrolLog) => Promise<string>;
|
|
7917
7917
|
};
|
|
7918
7918
|
|
|
7919
|
+
/**
|
|
7920
|
+
* Who may see, or change, one estate's guard-patrol record.
|
|
7921
|
+
*
|
|
7922
|
+
* An NFC patrol log is the proof that a round was actually walked: the route,
|
|
7923
|
+
* the day, each checkpoint the guard tapped a tag against, the ones that were
|
|
7924
|
+
* skipped and why, and the signature that closes the round off. It is the
|
|
7925
|
+
* document a client is shown when they ask whether their estate was patrolled.
|
|
7926
|
+
*
|
|
7927
|
+
* Every route on `/api/nfc-patrol-log` carried `requireAuth` and nothing more,
|
|
7928
|
+
* and this controller held no authorization identifier of any kind.
|
|
7929
|
+
* `requireAuth` proves a session exists; it makes no organisation or site
|
|
7930
|
+
* decision. So being signed in anywhere on the platform was enough to read
|
|
7931
|
+
* another client's patrol record, mark a checkpoint on it as completed or
|
|
7932
|
+
* skipped, or sign a round off as done - which is falsifying the estate's
|
|
7933
|
+
* patrol proof, not merely reading it.
|
|
7934
|
+
*
|
|
7935
|
+
* The estate is the scope, and the rule is the existing `requireSiteReach` ->
|
|
7936
|
+
* `entitleSite` that `/api/occurrence-books`, `/api/occurrence-entries`,
|
|
7937
|
+
* `/api/occurrence-subjects`, `/api/work-orders`, `/api/feedbacks` and the rest
|
|
7938
|
+
* already use. The `customer.sites` branch is load-bearing here more than
|
|
7939
|
+
* anywhere: the people who walk these rounds are the contracted security
|
|
7940
|
+
* agency's guards and hold no membership in the estate's own organisation.
|
|
7941
|
+
*
|
|
7942
|
+
* - `POST /` and `GET /` already REQUIRE a site (`schemaCreateNfcPatrolLog`
|
|
7943
|
+
* and the query schema both have `site` required), so the guard checks a
|
|
7944
|
+
* site the request already names and no caller's contract changes.
|
|
7945
|
+
* - `PATCH /:id/checkpoint`, `GET /:id` and `PATCH /:id/sign` name only a
|
|
7946
|
+
* record, so the site is read off the STORED log via
|
|
7947
|
+
* `requireNfcPatrolLogReach`.
|
|
7948
|
+
*
|
|
7949
|
+
* None of the three record routes can MOVE a log: `updateCheckpoint` sets only
|
|
7950
|
+
* the checkpoint fields and `sign` sets only the signature fields, so there is
|
|
7951
|
+
* no destination to check.
|
|
7952
|
+
*/
|
|
7919
7953
|
declare function useNfcPatrolLogController(): {
|
|
7920
7954
|
add: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
7921
7955
|
getAllBySite: (req: Request, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
|
package/dist/index.js
CHANGED
|
@@ -72788,6 +72788,10 @@ var import_joi119 = __toESM(require("joi"));
|
|
|
72788
72788
|
function useNfcPatrolLogController() {
|
|
72789
72789
|
const { add: _add, completeCheckpoint: _completeCheckpoint, sign: _sign } = useNfcPatrolLogService();
|
|
72790
72790
|
const { getAllBySite: _getAllBySite, getById: _getById } = useNfcPatrolLogRepo();
|
|
72791
|
+
async function requireNfcPatrolLogReach(req, id) {
|
|
72792
|
+
const log = await _getById(id);
|
|
72793
|
+
await requireSiteReach(req, log?.site);
|
|
72794
|
+
}
|
|
72791
72795
|
async function add(req, res, next) {
|
|
72792
72796
|
try {
|
|
72793
72797
|
const payload = req.body;
|
|
@@ -72795,7 +72799,8 @@ function useNfcPatrolLogController() {
|
|
|
72795
72799
|
if (error) {
|
|
72796
72800
|
return next(new import_node_server_utils227.BadRequestError(error.message));
|
|
72797
72801
|
}
|
|
72798
|
-
|
|
72802
|
+
await requireSiteReach(req, value.site);
|
|
72803
|
+
const data = await _add({ ...value, createdBy: callerId(req) });
|
|
72799
72804
|
res.status(201).json(data);
|
|
72800
72805
|
return;
|
|
72801
72806
|
} catch (error) {
|
|
@@ -72844,6 +72849,7 @@ function useNfcPatrolLogController() {
|
|
|
72844
72849
|
);
|
|
72845
72850
|
}
|
|
72846
72851
|
try {
|
|
72852
|
+
await requireSiteReach(req, value.site);
|
|
72847
72853
|
const data = await _getAllBySite({
|
|
72848
72854
|
page: value.page,
|
|
72849
72855
|
limit: value.limit,
|
|
@@ -72866,6 +72872,7 @@ function useNfcPatrolLogController() {
|
|
|
72866
72872
|
skippedRemarks: req.body.skippedRemarks
|
|
72867
72873
|
};
|
|
72868
72874
|
try {
|
|
72875
|
+
await requireNfcPatrolLogReach(req, id);
|
|
72869
72876
|
const data = await _completeCheckpoint(id, payload);
|
|
72870
72877
|
res.status(200).json(data);
|
|
72871
72878
|
return;
|
|
@@ -72876,6 +72883,7 @@ function useNfcPatrolLogController() {
|
|
|
72876
72883
|
}
|
|
72877
72884
|
async function getLog(req, res, next) {
|
|
72878
72885
|
try {
|
|
72886
|
+
await requireNfcPatrolLogReach(req, req.params.id);
|
|
72879
72887
|
const log = await _getById(req.params.id);
|
|
72880
72888
|
res.json(log);
|
|
72881
72889
|
} catch (error) {
|
|
@@ -72885,6 +72893,7 @@ function useNfcPatrolLogController() {
|
|
|
72885
72893
|
async function sign(req, res, next) {
|
|
72886
72894
|
try {
|
|
72887
72895
|
const value = await schemaSignNfcPatrolLog.validateAsync(req.body);
|
|
72896
|
+
await requireNfcPatrolLogReach(req, req.params.id);
|
|
72888
72897
|
const result = await _sign(
|
|
72889
72898
|
req.params.id,
|
|
72890
72899
|
value
|