@7365admin1/core 3.64.5-staging.305 → 3.64.5-staging.306

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,31 @@
1
+ ---
2
+ "@7365admin1/core": patch
3
+ ---
4
+
5
+ POST /api/people: count cross-client person creation (gate ships switched off)
6
+
7
+ The signed-in branch of `POST /api/people` checked only that somebody was signed
8
+ in. The site and organisation came straight from the request body, and where that
9
+ body carries an e-mail, a password and a resident/tenant type, the service goes on
10
+ to create a **user account** and an **active membership** as well as the person row.
11
+
12
+ `site.people` is the collection every site-reach decision on the platform reads its
13
+ verdict from, so a row written there is not an ordinary record — it manufactures
14
+ reach into another client's estate.
15
+
16
+ This adds the site-reach check the camera, HID and people-list paths already use,
17
+ behind `PEOPLE_CREATE_REACH_MODE`, and **ships it in `log` mode**: it records what
18
+ it would refuse and refuses nothing. `enforce` refuses; `off` does not look. Any
19
+ unexpected error allows the request — this must never be why a real registration
20
+ fails.
21
+
22
+ It ships off because of RP-5. A person row carries no `createdBy`, so who created
23
+ the existing rows cannot be established retrospectively and the true refusal count
24
+ is only knowable from live traffic. Measured read-only on staging: 372
25
+ `site.people` rows, of which exactly **1** is self-signup shaped (`platform:
26
+ "mobile"`, `status: "pending"`) and 371 were written by a signed-in caller, across
27
+ 3 of 256 sites.
28
+
29
+ Pre-login self-signup is untouched in every mode: a request with no session takes
30
+ the other branch of `add` before this runs, and that route is already constrained
31
+ by its own allow-list schema with `status` and `platform` pinned server-side.
package/dist/index.js CHANGED
@@ -62478,6 +62478,21 @@ var import_joi72 = __toESM(require("joi"));
62478
62478
  var import_node_server_utils148 = require("@7365admin1/node-server-utils");
62479
62479
  var import_node_server_utils149 = require("@7365admin1/node-server-utils");
62480
62480
 
62481
+ // src/utils/people-create-gate.util.ts
62482
+ var PEOPLE_CREATE_REACH_MODE_DEFAULT = "log";
62483
+ function peopleCreateReachMode(env2 = process.env) {
62484
+ return gateMode(
62485
+ env2.PEOPLE_CREATE_REACH_MODE,
62486
+ ["log", "enforce", "off"],
62487
+ PEOPLE_CREATE_REACH_MODE_DEFAULT
62488
+ );
62489
+ }
62490
+ function peopleCreateVerdict(params) {
62491
+ if (params.mode === "off" || params.reachable)
62492
+ return { refuse: false, record: false };
62493
+ return { refuse: params.mode === "enforce", record: true };
62494
+ }
62495
+
62481
62496
  // src/utils/self-signup-files.util.ts
62482
62497
  function dropUnsentFiles(body) {
62483
62498
  if (!Array.isArray(body?.files))
@@ -62587,6 +62602,41 @@ function usePersonController() {
62587
62602
  next(new import_node_server_utils148.BadRequestError(messages));
62588
62603
  return;
62589
62604
  }
62605
+ try {
62606
+ const mode = peopleCreateReachMode();
62607
+ if (mode !== "off") {
62608
+ const actor = await resolveInviteActor(callerId(req));
62609
+ let reachable = true;
62610
+ if (!staffMayBypass(actor)) {
62611
+ const site = payload?.site ? String(payload.site) : "";
62612
+ const org = payload?.org ? String(payload.org) : "";
62613
+ if (site) {
62614
+ reachable = await useCameraViewService().entitleSite({ siteId: site, userId: actor.id }).then(
62615
+ () => true,
62616
+ () => false
62617
+ );
62618
+ } else if (org) {
62619
+ reachable = actor.orgIds.includes(org);
62620
+ }
62621
+ }
62622
+ const verdict = peopleCreateVerdict({ mode, reachable });
62623
+ if (verdict.record) {
62624
+ import_node_server_utils148.logger.log({
62625
+ level: "info",
62626
+ message: `[people-create] ${verdict.refuse ? "refused" : "would-refuse"} caller=${actor.id} site=${payload?.site ?? ""} org=${payload?.org ?? ""} mode=${mode}`
62627
+ });
62628
+ }
62629
+ if (verdict.refuse) {
62630
+ next(new import_node_server_utils149.UnauthorizedError("Not authorized."));
62631
+ return;
62632
+ }
62633
+ }
62634
+ } catch (gateError) {
62635
+ import_node_server_utils148.logger.log({
62636
+ level: "warn",
62637
+ message: `[people-create] reach check failed open: ${gateError?.message}`
62638
+ });
62639
+ }
62590
62640
  try {
62591
62641
  const data = await _add(payload);
62592
62642
  res.status(201).json({ message: data });