@7365admin1/core 3.48.1-staging.194 → 3.48.1-staging.196

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 attendance settings routes to their estate. `GET` and `PUT /api/attendance-settings/site/:site` carried `requireAuth` and nothing more and the controller held no authorization identifier, so any signed-in account on the platform could read and rewrite any estate's attendance settings — turn location capture off, move the clock-in coordinates, or widen the geofence radius so a shift can be clocked in from anywhere. The read also created a default settings row for a site that had none, so an unauthorised read wrote into another estate's data. Reach is now decided from the session, on the site named in the request, before either the read or the write happens.
@@ -0,0 +1,33 @@
1
+ ---
2
+ "@7365admin1/core": patch
3
+ ---
4
+
5
+ Return the organisation's name only on the anonymous organisation lookup.
6
+
7
+ `GET /api/organizations/org/id/:id` is the unguarded twin of `/id/:id`, and it
8
+ shares this controller's `getById`. The handler answered `res.json(data)` on the
9
+ raw stored organisation document, which carries the client's own business
10
+ `email` and `contact` number alongside `description`, `busInst`, `nature`,
11
+ `defaultSite`, `terms` and `policies`. So anyone at all, with no session, who
12
+ knew or guessed an organisation id got a client's business contact details back.
13
+
14
+ It cannot be given a guard. Its only callers anywhere in the organisation are
15
+ `web-app-main`'s `sign-up-invite/onboarding-admin` and
16
+ `sign-up-invite/service-provider` pages (via `layer-common`'s
17
+ `useOrg.getOrgById`), reached from an invite e-mail before the account being
18
+ invited exists. Both pages do the same thing with the reply:
19
+ `form.organization = org?.name || ""` — they read `name` and nothing else.
20
+
21
+ So the anonymous path now uses a new `getNameById` on the organisation
22
+ repository: `findOne` with an **allow-list** projection of `_id` and `name`. A
23
+ field added to the organisation document later is excluded by default rather
24
+ than leaking on the day it is added.
25
+
26
+ `getById` itself is untouched. About twenty callers inside this package need the
27
+ whole document — `customer`, `feedback`, `member`, `person`, `robot`,
28
+ `service-provider`, `service-provider-invite`, `site` and `user-v2` services all
29
+ read it — so projecting that function would have broken them. The new function
30
+ sits beside it, and the controller picks between the two on whether the request
31
+ carries a session (`callerId(req)`), which is the same shape used for the
32
+ pre-login user lookup and resident-create routes. The guarded `/id/:id` route is
33
+ therefore unchanged: still the whole document, still 401 without a session.
package/dist/index.d.ts CHANGED
@@ -714,6 +714,7 @@ declare function useOrgRepo(): {
714
714
  pageRange: string;
715
715
  } | TOrg>;
716
716
  getById: (_id?: string | ObjectId) => Promise<TOrg>;
717
+ getNameById: (_id?: string | ObjectId) => Promise<mongodb.WithId<bson.Document>>;
717
718
  getByName: (name: string) => Promise<TOrg>;
718
719
  getByEmail: (email: string) => Promise<TOrg | null>;
719
720
  updateFieldById: ({ _id, field, value, }: {
package/dist/index.js CHANGED
@@ -11718,6 +11718,21 @@ function useOrgRepo() {
11718
11718
  throw error;
11719
11719
  }
11720
11720
  }
11721
+ async function getNameById(_id) {
11722
+ try {
11723
+ _id = new import_mongodb18.ObjectId(_id);
11724
+ } catch (error) {
11725
+ throw new import_node_server_utils20.BadRequestError("Invalid organization ID format.");
11726
+ }
11727
+ const data = await collection.findOne(
11728
+ { _id },
11729
+ { projection: { _id: 1, name: 1 } }
11730
+ );
11731
+ if (!data) {
11732
+ throw new import_node_server_utils20.NotFoundError("Organization not found.");
11733
+ }
11734
+ return data;
11735
+ }
11721
11736
  async function getById(_id) {
11722
11737
  try {
11723
11738
  _id = new import_mongodb18.ObjectId(_id);
@@ -12118,6 +12133,7 @@ function useOrgRepo() {
12118
12133
  update,
12119
12134
  getAll,
12120
12135
  getById,
12136
+ getNameById,
12121
12137
  getByName,
12122
12138
  getByEmail,
12123
12139
  updateFieldById,
@@ -27644,6 +27660,7 @@ function useOrgController() {
27644
27660
  const {
27645
27661
  getByName: _getByName,
27646
27662
  getById: _getById,
27663
+ getNameById: _getNameById,
27647
27664
  getByEmail: _getByEmail,
27648
27665
  getAll: _getAll,
27649
27666
  add: _add,
@@ -27815,7 +27832,7 @@ function useOrgController() {
27815
27832
  return;
27816
27833
  }
27817
27834
  try {
27818
- const data = await _getById(_id);
27835
+ const data = callerId(req) ? await _getById(_id) : await _getNameById(_id);
27819
27836
  res.json(data);
27820
27837
  return;
27821
27838
  } catch (error2) {
@@ -40522,6 +40539,9 @@ var import_node_server_utils121 = require("@7365admin1/node-server-utils");
40522
40539
  function useAttendanceSettingsController() {
40523
40540
  const { getAttendanceSettingsBySite: _getAttendanceSettingsBySite } = useAttendanceSettingsService();
40524
40541
  const { updateAttendanceSettings: _updateAttendanceSettings } = useAttendanceSettingsRepository();
40542
+ async function requireAttendanceSettingsReach(req, site) {
40543
+ await requireSiteReach(req, site);
40544
+ }
40525
40545
  async function getAttendanceSettingsBySite(req, res, next) {
40526
40546
  const query2 = { ...req.query, ...req.params };
40527
40547
  const validation = import_joi60.default.object({
@@ -40537,6 +40557,7 @@ function useAttendanceSettingsController() {
40537
40557
  const site = req.params.site ?? "";
40538
40558
  const serviceType = req.query.serviceType ?? "";
40539
40559
  try {
40560
+ await requireAttendanceSettingsReach(req, site);
40540
40561
  const data = await _getAttendanceSettingsBySite(site, serviceType);
40541
40562
  res.json(data);
40542
40563
  return;
@@ -40555,6 +40576,7 @@ function useAttendanceSettingsController() {
40555
40576
  return;
40556
40577
  }
40557
40578
  try {
40579
+ await requireAttendanceSettingsReach(req, payload.site);
40558
40580
  await _updateAttendanceSettings(payload.site, payload);
40559
40581
  res.json({ message: "Attendance settings updated successfully." });
40560
40582
  return;