@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.
@@ -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
- const data = await _add(value);
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