@7365admin1/core 3.48.1-staging.150 → 3.48.1-staging.151

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,45 @@
1
+ ---
2
+ "@7365admin1/core": minor
3
+ ---
4
+
5
+ Scope the user record routes to the person themselves, or Seven365 staff — on both `/api/users` and `/api/users/v2`.
6
+
7
+ Five routes on each mount carried `requireAuth` and nothing more, with the target
8
+ user id taken straight out of the URL:
9
+
10
+ - `GET /api/users/id/:id` returned any account's name, email, contact and NRIC.
11
+ - `GET /api/users/organization` took the organisation from the caller's own query
12
+ string, so any signed-in account could page through any client's whole people
13
+ directory.
14
+ - `PATCH /api/users/birthday/:id` rewrote any account's date of birth.
15
+ - `PATCH /api/users/field/:id` is the sharp one. Its allow-list includes `email`,
16
+ `nric`, `status` and `defaultOrg`, so pointing it at somebody else's id rewrote
17
+ **their sign-in address** — and the forgot-password flow then delivers to the
18
+ new one. That is account takeover, and `status` on the same route disables an
19
+ account.
20
+ - `PATCH /api/users/password/:id` verifies the current password inside the
21
+ service, so it was an online **guessing oracle** against any account: a wrong
22
+ current password and a mismatched confirmation answered differently.
23
+
24
+ `/api/users/v2` is a separate controller file (`user-v2.controller.ts`) with the
25
+ same routes and the same five holes, so the guard was added to
26
+ `console-authz.util.ts` as `requireSelfOrPlatformStaff` and **both** controllers
27
+ call it — rather than fixing v1 and leaving its twin open.
28
+
29
+ - the four id-in-URL routes require the id to be the caller's own, or the caller
30
+ to be Seven365 staff.
31
+ - `GET /api/users/organization` puts the caller-supplied organisation through
32
+ `requireOrgAccess` before it is used, the same way `GET /api/members` does.
33
+
34
+ Guards sit after each handler's Joi validation, so a malformed id or a malformed
35
+ value still answers 400 exactly as it does today.
36
+
37
+ Every caller in the org already satisfies this rule: the account app's
38
+ personal-info screens, the resident mobile app's profile editor, and the sign-in
39
+ and verify-email flows all pass the caller's own id. The one screen that edits
40
+ another person, `layer-common ClientDetailForm.vue:370`, is reached only from
41
+ `web-app-org pages/super-admin/client-list.vue` — the Seven365 staff console —
42
+ which the staff branch covers.
43
+
44
+ Nothing changes for a permitted caller: same response shape, same fields, same
45
+ status.
package/dist/index.js CHANGED
@@ -18153,6 +18153,13 @@ async function requirePlatformStaff(req) {
18153
18153
  }
18154
18154
  return id;
18155
18155
  }
18156
+ async function requireSelfOrPlatformStaff(req, userId) {
18157
+ const id = callerId(req);
18158
+ const target = userId?.toString() ?? "";
18159
+ if (id && target && target === id)
18160
+ return id;
18161
+ return await requirePlatformStaff(req);
18162
+ }
18156
18163
  async function requireOrgAccess(req, orgId) {
18157
18164
  const id = callerId(req);
18158
18165
  if (!id)
@@ -18200,6 +18207,7 @@ function useUserController() {
18200
18207
  return;
18201
18208
  }
18202
18209
  try {
18210
+ await requireSelfOrPlatformStaff(req, _id);
18203
18211
  const user = await _getById(_id);
18204
18212
  res.json(user);
18205
18213
  return;
@@ -18228,6 +18236,7 @@ function useUserController() {
18228
18236
  const status = req.query.status ?? "active";
18229
18237
  const organization = req.query.organization ?? "";
18230
18238
  try {
18239
+ await requireOrgAccess(req, organization);
18231
18240
  const data = await _getUsersByOrgId({
18232
18241
  search,
18233
18242
  page,
@@ -18373,6 +18382,7 @@ function useUserController() {
18373
18382
  return;
18374
18383
  }
18375
18384
  try {
18385
+ await requireSelfOrPlatformStaff(req, _id);
18376
18386
  const message = await _updateBirthday({ _id, ...payload });
18377
18387
  res.json({ message });
18378
18388
  return;
@@ -18414,6 +18424,7 @@ function useUserController() {
18414
18424
  return;
18415
18425
  }
18416
18426
  try {
18427
+ await requireSelfOrPlatformStaff(req, _id);
18417
18428
  let message;
18418
18429
  if (payload.field === "plateNumber") {
18419
18430
  message = await updatePlateNumberByUserId(
@@ -18455,6 +18466,7 @@ function useUserController() {
18455
18466
  const newPassword = req.body.newPassword ?? "";
18456
18467
  const passwordConfirmation = req.body.passwordConfirmation ?? "";
18457
18468
  try {
18469
+ await requireSelfOrPlatformStaff(req, _id);
18458
18470
  await _updatePasswordById(
18459
18471
  _id,
18460
18472
  currentPassword,
@@ -80951,6 +80963,7 @@ function useUserControllerV2() {
80951
80963
  return;
80952
80964
  }
80953
80965
  try {
80966
+ await requireSelfOrPlatformStaff(req, value);
80954
80967
  const user = await _getById(value);
80955
80968
  res.json(user);
80956
80969
  return;
@@ -81013,6 +81026,7 @@ function useUserControllerV2() {
81013
81026
  if (error)
81014
81027
  return rejectValidation(error, next);
81015
81028
  try {
81029
+ await requireOrgAccess(req, value.organization);
81016
81030
  res.json(
81017
81031
  await _getUsersByOrgId({
81018
81032
  search: value.search ?? "",
@@ -81111,6 +81125,7 @@ function useUserControllerV2() {
81111
81125
  return;
81112
81126
  }
81113
81127
  try {
81128
+ await requireSelfOrPlatformStaff(req, _id);
81114
81129
  const message = await _updateBirthday({ _id, ...payload });
81115
81130
  res.json({ message });
81116
81131
  return;
@@ -81142,6 +81157,7 @@ function useUserControllerV2() {
81142
81157
  return;
81143
81158
  }
81144
81159
  try {
81160
+ await requireSelfOrPlatformStaff(req, _id);
81145
81161
  const message = await _updateUserFieldById({ _id, ...payload });
81146
81162
  res.json({ message });
81147
81163
  return;
@@ -81172,6 +81188,7 @@ function useUserControllerV2() {
81172
81188
  const newPassword = req.body.newPassword ?? "";
81173
81189
  const passwordConfirmation = req.body.passwordConfirmation ?? "";
81174
81190
  try {
81191
+ await requireSelfOrPlatformStaff(req, _id);
81175
81192
  await _updatePasswordById(
81176
81193
  _id,
81177
81194
  currentPassword,