@7365admin1/core 3.48.1-staging.206 → 3.48.1-staging.207

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,39 @@
1
+ ---
2
+ "@7365admin1/core": minor
3
+ ---
4
+
5
+ Scope the organisation list to the caller, and put the promo-code list behind the staff gate.
6
+
7
+ `GET /api/organizations` and `GET /api/promo-codes` performed no authorization
8
+ at all — `requireAuth` and nothing more — while every sibling handler in the
9
+ same two files guarded. So any signed-in account on the platform, a resident or
10
+ a cleaner included, could page through the name, e-mail, nature and status of
11
+ **every Seven365 client**, and read off **every discount code** we have ever
12
+ minted, with its rate and its expiry, and then redeem whichever is worth most.
13
+
14
+ The two rules are different, because the callers are different.
15
+
16
+ **`GET /api/organizations` is now a SCOPE, not a refusal.** Seven365 staff see
17
+ every organisation exactly as before; anybody else sees the organisations they
18
+ hold a live membership of, plus any registered under their own verified account
19
+ e-mail. The e-mail half is not decoration: `web-app-main
20
+ pages/verify/email/index.vue:322` — the org-owner self-signup — lists
21
+ organisations to find the one it created a line earlier, at which point that
22
+ brand-new client owner is a member of nothing. Refusing them would have traded
23
+ the leak for a broken signup. The comparison is the one
24
+ `getOrgsByEmail` already makes, against the address on the caller's own `users`
25
+ row; nothing the caller sends takes part in it. It also removes a real bug from
26
+ that flow: the page takes `items[0]` of a newest-first list, so two client
27
+ owners signing up in the same moment could be handed each other's organisation
28
+ as their `defaultOrg`.
29
+
30
+ The scope is part of the Redis cache key as well as the query, so one caller's
31
+ narrowed page is never replayed to the next, and `countDocuments` runs the
32
+ scoped query so pagination cannot walk past the scope.
33
+
34
+ **`GET /api/promo-codes` is Seven365 staff only,** together with
35
+ `GET /api/promo-codes/id/:id`, which is the same disclosure one record at a
36
+ time. Both are read solely by the staff console. A client at checkout is
37
+ unaffected: `web-app-org pages/checkout.vue` and
38
+ `subscription/manage-seats.vue` type a code and validate it through
39
+ `GET /api/promo-codes/code`, which stays signed-in-only and is untouched.
package/dist/index.d.ts CHANGED
@@ -702,13 +702,17 @@ declare function useOrgRepo(): {
702
702
  createUniqueIndex: () => Promise<void>;
703
703
  add: (value: TOrg, session?: ClientSession) => Promise<ObjectId>;
704
704
  update: (id: string, value: Partial<TOrg>, session?: ClientSession) => Promise<string>;
705
- getAll: ({ search, page, limit, sort, status, nature, }: {
705
+ getAll: ({ search, page, limit, sort, status, nature, scope, }: {
706
706
  search?: string | undefined;
707
707
  page?: number | undefined;
708
708
  limit?: number | undefined;
709
709
  sort?: Record<string, number> | undefined;
710
710
  status?: string | undefined;
711
711
  nature?: string | undefined;
712
+ scope?: {
713
+ orgIds?: string[] | undefined;
714
+ email?: string | undefined;
715
+ } | undefined;
712
716
  }) => Promise<{
713
717
  items: any[];
714
718
  pages: number;
package/dist/index.js CHANGED
@@ -11739,7 +11739,8 @@ function useOrgRepo() {
11739
11739
  limit = 10,
11740
11740
  sort = {},
11741
11741
  status = "active",
11742
- nature = ""
11742
+ nature = "",
11743
+ scope
11743
11744
  }) {
11744
11745
  const query2 = { status };
11745
11746
  sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
@@ -11749,6 +11750,22 @@ function useOrgRepo() {
11749
11750
  page,
11750
11751
  limit
11751
11752
  };
11753
+ if (scope) {
11754
+ const ids2 = Array.from(
11755
+ new Set((scope.orgIds ?? []).filter((id) => import_mongodb19.ObjectId.isValid(id)))
11756
+ ).sort();
11757
+ const email = (scope.email ?? "").trim();
11758
+ const reach = [];
11759
+ if (ids2.length)
11760
+ reach.push({ _id: { $in: ids2.map((id) => new import_mongodb19.ObjectId(id)) } });
11761
+ if (email)
11762
+ reach.push({ email });
11763
+ if (!reach.length) {
11764
+ return (0, import_node_server_utils21.paginate)([], page > 0 ? page - 1 : 0, limit, 0);
11765
+ }
11766
+ query2.$or = reach;
11767
+ cacheOptions.scope = `${ids2.join(",")}|${email.toLowerCase()}`;
11768
+ }
11752
11769
  if (search) {
11753
11770
  query2.$text = { $search: search };
11754
11771
  cacheOptions.search = search;
@@ -27890,11 +27907,19 @@ function useOrgController() {
27890
27907
  const limit = parseInt(req.query.limit ?? "10");
27891
27908
  const nature = req.query.nature ?? "";
27892
27909
  try {
27910
+ const id = callerId(req);
27911
+ const actor = await resolveInviteActor(id);
27912
+ let scope;
27913
+ if (!actor.isSuperAdmin) {
27914
+ const self = await _getUserById(id).catch(() => null);
27915
+ scope = { orgIds: actor.orgIds, email: self?.email ?? "" };
27916
+ }
27893
27917
  const data = await _getAll({
27894
27918
  search,
27895
27919
  page,
27896
27920
  limit,
27897
- nature
27921
+ nature,
27922
+ scope
27898
27923
  });
27899
27924
  res.json(data);
27900
27925
  return;
@@ -31336,6 +31361,7 @@ function usePromoCodeController() {
31336
31361
  return;
31337
31362
  }
31338
31363
  try {
31364
+ await requirePlatformStaff(req);
31339
31365
  const data = await _getById(_id);
31340
31366
  res.json(data);
31341
31367
  return;
@@ -31364,6 +31390,7 @@ function usePromoCodeController() {
31364
31390
  const limit = parseInt(req.query.limit ?? "10");
31365
31391
  const status = req.query.status ?? "active";
31366
31392
  try {
31393
+ await requirePlatformStaff(req);
31367
31394
  const data = await _getPromoCodes({
31368
31395
  search,
31369
31396
  page,