@7365admin1/core 3.64.5-staging.293 → 3.64.5-staging.295

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
+ POST /api/members/direct refuses (400) a role that belongs to a different organisation, unless the caller already holds that role in the organisation being joined.
@@ -0,0 +1,5 @@
1
+ ---
2
+ "@7365admin1/core": patch
3
+ ---
4
+
5
+ PATCH /api/users/field/:id (and v2): on your own account you may change only name, contact, nric, dateOfBirth, profile and gender, and defaultOrg to an organisation you reach. status, email and plateNumber are staff-only.
package/dist/index.d.ts CHANGED
@@ -11138,7 +11138,9 @@ declare function hasOrgOwnership(userId?: string | ObjectId | null, orgId?: stri
11138
11138
  * `status: { $ne: "deleted" }` is the same liveness test every other membership
11139
11139
  * read in this package uses, so a revoked membership stops conferring the read.
11140
11140
  */
11141
- declare function holdsRole(userId?: string | ObjectId | null, roleId?: string | ObjectId | null): Promise<boolean>;
11141
+ declare function holdsRole(userId?: string | ObjectId | null, roleId?: string | ObjectId | null,
11142
+ /** when given, the live membership must also be in THIS organisation */
11143
+ orgId?: string | ObjectId | null): Promise<boolean>;
11142
11144
 
11143
11145
  declare function useAuthControllerV2(): {
11144
11146
  signUp: (req: Request, res: Response, next: NextFunction) => Promise<void>;
package/dist/index.js CHANGED
@@ -14627,19 +14627,24 @@ async function hasOrgOwnership(userId, orgId) {
14627
14627
  );
14628
14628
  return Boolean(found);
14629
14629
  }
14630
- async function holdsRole(userId, roleId) {
14630
+ async function holdsRole(userId, roleId, orgId) {
14631
14631
  const id = userId?.toString() ?? "";
14632
14632
  const role = roleId?.toString() ?? "";
14633
+ const org = orgId?.toString() ?? "";
14633
14634
  if (!id || !role || !import_mongodb25.ObjectId.isValid(id) || !import_mongodb25.ObjectId.isValid(role)) {
14634
14635
  return false;
14635
14636
  }
14637
+ if (orgId !== void 0 && !import_mongodb25.ObjectId.isValid(org))
14638
+ return false;
14636
14639
  const db2 = import_node_server_utils27.useAtlas.getDb();
14637
14640
  if (!db2)
14638
14641
  return false;
14639
14642
  const member = await db2.collection("members").findOne({
14640
14643
  user: new import_mongodb25.ObjectId(id),
14641
14644
  role: new import_mongodb25.ObjectId(role),
14642
- status: { $ne: "deleted" }
14645
+ status: { $ne: "deleted" },
14646
+ // both shapes, as `hasOrgInvitation` does for `metadata.org`
14647
+ ...org ? { org: { $in: [new import_mongodb25.ObjectId(org), org] } } : {}
14643
14648
  });
14644
14649
  return Boolean(member);
14645
14650
  }
@@ -16122,11 +16127,32 @@ async function requireInviteReach(req, org, app, role) {
16122
16127
  await requireOrgReach(req, org);
16123
16128
  await requireRoleInvitable(role, org, apps);
16124
16129
  }
16125
- async function requireUserFieldWrite(req, userId, field, grant) {
16126
- if (field === "email")
16127
- return await requirePlatformStaff(req, grant);
16128
- return await requireSelfOrPlatformStaff(req, userId, grant);
16130
+ async function requireUserFieldWrite(req, userId, field, grant, value) {
16131
+ if (field && SELF_EDITABLE_USER_FIELDS.includes(field)) {
16132
+ return await requireSelfOrPlatformStaff(req, userId, grant);
16133
+ }
16134
+ if (field === "defaultOrg") {
16135
+ const id = callerId(req);
16136
+ const org = value?.toString?.() ?? "";
16137
+ if (id && userId?.toString() === id && org) {
16138
+ const reaches = await requireOrgReach(req, org).then(
16139
+ () => true,
16140
+ () => false
16141
+ );
16142
+ if (reaches)
16143
+ return id;
16144
+ }
16145
+ }
16146
+ return await requirePlatformStaff(req, grant);
16129
16147
  }
16148
+ var SELF_EDITABLE_USER_FIELDS = [
16149
+ "name",
16150
+ "contact",
16151
+ "nric",
16152
+ "dateOfBirth",
16153
+ "profile",
16154
+ "gender"
16155
+ ];
16130
16156
  async function requireVerificationListReach(req, orgId) {
16131
16157
  if (orgId) {
16132
16158
  await requireOrgReach(req, orgId);
@@ -19791,7 +19817,7 @@ function useUserController() {
19791
19817
  return;
19792
19818
  }
19793
19819
  try {
19794
- await requireUserFieldWrite(req, _id, payload.field, { resource: "users" });
19820
+ await requireUserFieldWrite(req, _id, payload.field, { resource: "users" }, payload.value);
19795
19821
  let message;
19796
19822
  if (payload.field === "plateNumber") {
19797
19823
  message = await updatePlateNumberByUserId(
@@ -20662,6 +20688,12 @@ function useMemberService() {
20662
20688
  if (targetRole?.type === PLATFORM_STAFF_MEMBER_TYPE) {
20663
20689
  await requireStaffCaller(callerId5, "assign a Seven365 staff role");
20664
20690
  }
20691
+ const roleOrg = targetRole?.org?.toString?.() ?? "";
20692
+ if (roleOrg && roleOrg !== (orgId?.toString() ?? "").toLowerCase() && !await holdsRole(callerId5, roleId, orgId)) {
20693
+ throw new import_node_server_utils50.BadRequestError(
20694
+ "That role belongs to a different organisation."
20695
+ );
20696
+ }
20665
20697
  const session = import_node_server_utils50.useAtlas.getClient()?.startSession();
20666
20698
  session?.startTransaction();
20667
20699
  try {
@@ -90210,7 +90242,7 @@ function useUserControllerV2() {
90210
90242
  return;
90211
90243
  }
90212
90244
  try {
90213
- await requireUserFieldWrite(req, _id, payload.field, { resource: "users" });
90245
+ await requireUserFieldWrite(req, _id, payload.field, { resource: "users" }, payload.value);
90214
90246
  const message = await _updateUserFieldById({ _id, ...payload });
90215
90247
  res.json({ message });
90216
90248
  return;