@7365admin1/core 3.48.1-staging.175 → 3.48.1-staging.177

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,5 @@
1
+ ---
2
+ "@7365admin1/core": patch
3
+ ---
4
+
5
+ Scope the site billing item routes to the estate, and check the unit list against the item's own site
@@ -0,0 +1,12 @@
1
+ ---
2
+ "@7365admin1/core": patch
3
+ ---
4
+
5
+ Scope the visitor guest routes to the estate
6
+
7
+ `/api/visitor-guests` carried `requireAuth` and nothing more on all four
8
+ routes, so any signed-in account could add, read, edit or delete another
9
+ estate's expected-visitor list. `site` was optional on the list query and the
10
+ repository only applies it when valid, so omitting it returned every estate's
11
+ visitor records at once. All four handlers now ask the existing site-reach
12
+ rule, and `site` is required on the list query.
package/dist/index.d.ts CHANGED
@@ -4693,6 +4693,7 @@ declare function useGuestManagementRepo(): {
4693
4693
  org?: string | undefined;
4694
4694
  site?: string | undefined;
4695
4695
  }, session?: ClientSession) => Promise<{}>;
4696
+ getById: (_id: string | ObjectId, session?: ClientSession) => Promise<mongodb.WithId<bson.Document> | null>;
4696
4697
  updateVisitorGuestById: (_id: string | ObjectId, value: Partial<TGuestManagement>, session?: ClientSession) => Promise<mongodb.UpdateResult<bson.Document>>;
4697
4698
  deleteVisitorGuest: (_id: string | ObjectId) => Promise<number>;
4698
4699
  createTextIndex: () => Promise<void>;
@@ -4703,6 +4704,25 @@ declare function useGuestManagementService(): {
4703
4704
  updateVisitorGuestById: (id: string | ObjectId, value: TGuestManagement) => Promise<string>;
4704
4705
  };
4705
4706
 
4707
+ /**
4708
+ * A visitor guest is a named person an estate expects at its gate: name,
4709
+ * contact, NRIC, vehicle plate, and the block/level/unit they are visiting.
4710
+ *
4711
+ * All four routes carried `requireAuth` and nothing more, and this controller
4712
+ * held no authorization identifier of any kind. `site` and `org` were read
4713
+ * straight off the caller — the body on `POST`, the query on `GET` — and never
4714
+ * compared to anything, and `PUT`/`DELETE` reached the row by id alone. Being
4715
+ * signed in ANYWHERE on the platform was therefore enough to read, edit or
4716
+ * delete any estate's guest list.
4717
+ *
4718
+ * The `GET` was the worst of the four: `site` and `org` were OPTIONAL, and the
4719
+ * repository only applies them `ObjectId.isValid(...)`. Omitting both left the
4720
+ * `$match` with no tenancy term at all, so one request returned every estate's
4721
+ * visitor records on the platform — names, NRIC, contact numbers and plates.
4722
+ *
4723
+ * The rule is the existing one, `requireSiteReach` (i.e. `entitleSite`), not a
4724
+ * second competing mechanism.
4725
+ */
4706
4726
  declare function useGuestManagementController(): {
4707
4727
  add: (req: Request, res: Response, next: NextFunction) => Promise<void>;
4708
4728
  getAll: (req: Request, res: Response, next: NextFunction) => Promise<void>;
@@ -6171,6 +6191,30 @@ declare function useSiteBillingItemRepo(): {
6171
6191
  createIndexes: () => Promise<string>;
6172
6192
  };
6173
6193
 
6194
+ /**
6195
+ * A billing item is a charge the estate raises on its residents — the name, the
6196
+ * amount, the tax that rides on it, how often it repeats, and which units it is
6197
+ * attached to.
6198
+ *
6199
+ * All five handlers carried `requireAuth` and nothing else, and the controller
6200
+ * held no authorization identifier of any kind. The record id came from the URL
6201
+ * and the site came from the caller's own query or body, so any signed-in
6202
+ * account on the platform could list another client's charges, add a charge to
6203
+ * their estate, change what a charge costs, MOVE a charge onto a different
6204
+ * estate (`site` is writable on the update schema), or delete it.
6205
+ *
6206
+ * The rule is the existing one — `requireSiteReach`, i.e. `entitleSite` — asked
6207
+ * about the site the request names on the way in, and about the STORED site for
6208
+ * the id routes, which name no site at all. `PUT` asks twice, once about where
6209
+ * the record is and once about where the body wants to move it, because a MOVE
6210
+ * needs its destination checked too. This is the same shape as
6211
+ * `site-billing-configuration.controller.ts`, not a second mechanism.
6212
+ *
6213
+ * The unit list is checked separately, in the service — see
6214
+ * `site-billing-item.service.ts`. Reaching a site is not the same question as
6215
+ * "do these units belong to it", and the service is where the unit ids are
6216
+ * actually consumed.
6217
+ */
6174
6218
  declare function useSiteBillingItemController(): {
6175
6219
  add: (req: Request, res: Response, next: NextFunction) => Promise<void>;
6176
6220
  getAll: (req: Request, res: Response, next: NextFunction) => Promise<void>;
package/dist/index.js CHANGED
@@ -50372,6 +50372,14 @@ function useGuestManagementRepo() {
50372
50372
  throw error;
50373
50373
  }
50374
50374
  }
50375
+ async function getById(_id, session) {
50376
+ try {
50377
+ _id = new import_mongodb86.ObjectId(_id);
50378
+ } catch (error) {
50379
+ throw new import_node_server_utils135.BadRequestError("Invalid visitor guest ID format.");
50380
+ }
50381
+ return await collection.findOne({ _id }, { session });
50382
+ }
50375
50383
  async function updateVisitorGuestById(_id, value, session) {
50376
50384
  try {
50377
50385
  _id = new import_mongodb86.ObjectId(_id);
@@ -50434,6 +50442,7 @@ function useGuestManagementRepo() {
50434
50442
  return {
50435
50443
  add,
50436
50444
  getAll,
50445
+ getById,
50437
50446
  updateVisitorGuestById,
50438
50447
  deleteVisitorGuest,
50439
50448
  createTextIndex
@@ -50483,7 +50492,11 @@ var import_joi67 = __toESM(require("joi"));
50483
50492
  var import_node_server_utils137 = require("@7365admin1/node-server-utils");
50484
50493
  function useGuestManagementController() {
50485
50494
  const { add: _add, updateVisitorGuestById: _updateVisitorGuestById } = useGuestManagementService();
50486
- const { getAll: _getAll, deleteVisitorGuest: _deleteVisitorGuest } = useGuestManagementRepo();
50495
+ const {
50496
+ getAll: _getAll,
50497
+ getById: _getById,
50498
+ deleteVisitorGuest: _deleteVisitorGuest
50499
+ } = useGuestManagementRepo();
50487
50500
  async function add(req, res, next) {
50488
50501
  const payload = { ...req.body };
50489
50502
  const { error } = schemaGuestManagement.validate(payload, {
@@ -50496,6 +50509,7 @@ function useGuestManagementController() {
50496
50509
  return;
50497
50510
  }
50498
50511
  try {
50512
+ await requireSiteReach(req, payload.site);
50499
50513
  const data = await _add(payload);
50500
50514
  res.status(201).json({ message: data });
50501
50515
  return;
@@ -50516,7 +50530,9 @@ function useGuestManagementController() {
50516
50530
  dateFrom: import_joi67.default.date().iso().optional().allow(null, ""),
50517
50531
  dateTo: import_joi67.default.date().iso().optional().allow(null, ""),
50518
50532
  org: import_joi67.default.string().optional().allow(null, ""),
50519
- site: import_joi67.default.string().optional().allow(null, "")
50533
+ // required, so the repository can never build a `$match` with no
50534
+ // tenancy term and return the whole platform's guest list
50535
+ site: import_joi67.default.string().hex().length(24).required()
50520
50536
  });
50521
50537
  const query2 = { ...req.query };
50522
50538
  const { error } = validation.validate(query2, {
@@ -50546,6 +50562,7 @@ function useGuestManagementController() {
50546
50562
  }
50547
50563
  });
50548
50564
  try {
50565
+ await requireSiteReach(req, site);
50549
50566
  const data = await _getAll({
50550
50567
  search,
50551
50568
  page,
@@ -50578,6 +50595,8 @@ function useGuestManagementController() {
50578
50595
  return;
50579
50596
  }
50580
50597
  try {
50598
+ const current = await _getById(_id);
50599
+ await requireSiteReach(req, current?.site);
50581
50600
  const result = await _updateVisitorGuestById(_id, req.body);
50582
50601
  res.status(200).json({ message: result });
50583
50602
  return;
@@ -50597,6 +50616,8 @@ function useGuestManagementController() {
50597
50616
  return;
50598
50617
  }
50599
50618
  try {
50619
+ const current = await _getById(_id);
50620
+ await requireSiteReach(req, current?.site);
50600
50621
  await _deleteVisitorGuest(_id);
50601
50622
  res.status(200).json({ message: "Successfully deleted visitor guest." });
50602
50623
  return;
@@ -58520,6 +58541,12 @@ function useSiteBillingItemService() {
58520
58541
  } = useSiteBillingItemRepo();
58521
58542
  const { updateById: _updateBuildingUnitById, getById: _getById } = useBuildingUnitRepo();
58522
58543
  const { getAll } = useSiteBillingConfigurationRepo();
58544
+ function assertUnitAtSite(buildingUnit, site) {
58545
+ const siteId = site?.toString?.() ?? "";
58546
+ if (!siteId || (buildingUnit?.site?.toString() ?? "") !== siteId) {
58547
+ throw new import_node_server_utils179.UnauthorizedError("Not authorized.");
58548
+ }
58549
+ }
58523
58550
  async function add(value) {
58524
58551
  const session = import_node_server_utils179.useAtlas.getClient()?.startSession();
58525
58552
  if (!session) {
@@ -58550,6 +58577,7 @@ function useSiteBillingItemService() {
58550
58577
  const promises = value.units.map(async (unit) => {
58551
58578
  const unitId = unit?._id?.toString() || "";
58552
58579
  const buildingUnit = await _getById(unitId);
58580
+ assertUnitAtSite(buildingUnit, value.site);
58553
58581
  const normalizedExistingBilling = (buildingUnit.billing || []).map(
58554
58582
  (bill) => ({
58555
58583
  _id: bill._id?.toString(),
@@ -58646,6 +58674,7 @@ function useSiteBillingItemService() {
58646
58674
  unit._id?.toString() || "",
58647
58675
  session
58648
58676
  );
58677
+ assertUnitAtSite(buildingUnit, value.site ?? billing_item?.site);
58649
58678
  const normalizedBilling = (buildingUnit.billing || []).map(
58650
58679
  (bill) => ({
58651
58680
  _id: bill._id?.toString() || "",
@@ -58686,7 +58715,7 @@ function useSiteBillingItemService() {
58686
58715
  message: error.message
58687
58716
  });
58688
58717
  await session.abortTransaction();
58689
- if (error instanceof import_node_server_utils179.BadRequestError) {
58718
+ if (error instanceof import_node_server_utils179.BadRequestError || error instanceof import_node_server_utils179.UnauthorizedError) {
58690
58719
  throw error;
58691
58720
  }
58692
58721
  throw new import_node_server_utils179.BadRequestError("Failed to update Site Billing Item.");
@@ -58717,6 +58746,7 @@ function useSiteBillingItemController() {
58717
58746
  return;
58718
58747
  }
58719
58748
  try {
58749
+ await requireSiteReach(req, value.site);
58720
58750
  const question = await _add(value);
58721
58751
  res.status(201).json({
58722
58752
  message: "Site Billing Item added successfully.",
@@ -58761,6 +58791,7 @@ function useSiteBillingItemController() {
58761
58791
  }
58762
58792
  });
58763
58793
  try {
58794
+ await requireSiteReach(req, site);
58764
58795
  const data = await _getAll({
58765
58796
  search,
58766
58797
  page,
@@ -58789,6 +58820,7 @@ function useSiteBillingItemController() {
58789
58820
  }
58790
58821
  try {
58791
58822
  const building = await _getById(id);
58823
+ await requireSiteReach(req, building?.site);
58792
58824
  res.json({
58793
58825
  message: "Successfully retrieved site billing item.",
58794
58826
  data: { building }
@@ -58811,6 +58843,10 @@ function useSiteBillingItemController() {
58811
58843
  return;
58812
58844
  }
58813
58845
  try {
58846
+ const current = await _getById(_id);
58847
+ await requireSiteReach(req, current?.site);
58848
+ if (req.body?.site)
58849
+ await requireSiteReach(req, req.body.site);
58814
58850
  const result = await _updateById(_id, req.body);
58815
58851
  res.status(200).json({ message: result });
58816
58852
  return;
@@ -58830,6 +58866,8 @@ function useSiteBillingItemController() {
58830
58866
  return;
58831
58867
  }
58832
58868
  try {
58869
+ const current = await _getById(_id);
58870
+ await requireSiteReach(req, current?.site);
58833
58871
  await _deleteById(_id);
58834
58872
  res.status(200).json({ message: "Successfully deleted site billing item." });
58835
58873
  return;