@7365admin1/core 3.64.3-staging.287 → 3.64.3-staging.289
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.
- package/.changeset/member-lookup-honours-org.md +17 -0
- package/.changeset/module-gate-foundation.md +20 -0
- package/dist/index.js +50 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +50 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/member-lookup-honours-org.test.mjs +44 -0
- package/test/module-gate.test.mjs +237 -0
- package/test/org-modules.test.mjs +78 -0
- package/test/site-modules.test.mjs +84 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/core": patch
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Use the organisation the app asks for when loading a user's membership.
|
|
6
|
+
|
|
7
|
+
Every mobile app loads its user's role through
|
|
8
|
+
`GET /api/members/user/:id/app/:type?org=<org>`, and `getByUserIdType` ignored
|
|
9
|
+
`org`, answering with the first membership of that app type it found. A person
|
|
10
|
+
in more than one organisation could therefore be handed another organisation's
|
|
11
|
+
membership in the app they were using.
|
|
12
|
+
|
|
13
|
+
It now prefers a LIVE membership in the requested organisation. When there is
|
|
14
|
+
none there (or only a removed one), or no valid `org` is sent, it answers exactly
|
|
15
|
+
as before, so no caller that received a membership now receives "not found", and
|
|
16
|
+
a removed membership never wins over a live one. Login and the other internal
|
|
17
|
+
uses of the repository lookup are unchanged.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/core": patch
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Lay the groundwork for module lists becoming a gate, without narrowing anyone.
|
|
6
|
+
|
|
7
|
+
- New `utils/module-gate.util.ts`: the list of modules a client's list can
|
|
8
|
+
govern (generated from layer-common 5a96f95), the spelling groups, the
|
|
9
|
+
always-included set, and pure functions that work out which modules a
|
|
10
|
+
caller's apps must hide (`deniedModules`) and who would lose what if a list
|
|
11
|
+
changed (`moduleImpact`). Nothing calls them yet.
|
|
12
|
+
- The organisation and site module checks now treat every spelling of a module
|
|
13
|
+
as the same module (ticking `workOrder` also allows `work_orders`,
|
|
14
|
+
`work_order` and `work-orders`), and never refuse members, invitations, roles,
|
|
15
|
+
sites, customers, subscriptions, payment methods or site settings. This only
|
|
16
|
+
ever refuses less than before.
|
|
17
|
+
- A Site Settings save (`PATCH /api/sites/id/:id`) now always keeps the site's
|
|
18
|
+
stored module list. Before this, the save replaced `metadata` wholesale and
|
|
19
|
+
could wipe or overwrite that list. `PATCH /api/sites/:id/modules` is the only
|
|
20
|
+
way to change it.
|
package/dist/index.js
CHANGED
|
@@ -11689,13 +11689,42 @@ function normaliseModules(modules) {
|
|
|
11689
11689
|
}
|
|
11690
11690
|
return out;
|
|
11691
11691
|
}
|
|
11692
|
+
var ALWAYS_ALLOWED = Object.freeze([
|
|
11693
|
+
"sites",
|
|
11694
|
+
"subscriptions",
|
|
11695
|
+
"customers",
|
|
11696
|
+
"payment-methods",
|
|
11697
|
+
"members",
|
|
11698
|
+
"invitations",
|
|
11699
|
+
"roles",
|
|
11700
|
+
"roles-and-permissions",
|
|
11701
|
+
"site-settings"
|
|
11702
|
+
]);
|
|
11703
|
+
var SPELLING_GROUPS = Object.freeze(
|
|
11704
|
+
[
|
|
11705
|
+
["work_orders", "workOrder", "work_order", "work-orders"],
|
|
11706
|
+
["visitor-mgmt", "visitorManagement"],
|
|
11707
|
+
["bulletin-board", "bulletin-board-mgmt"],
|
|
11708
|
+
["facility-booking-mgmt", "facility-booking"],
|
|
11709
|
+
["feedbacks", "feedback"],
|
|
11710
|
+
["building-mgmt", "buildingManagement"],
|
|
11711
|
+
["roles", "roles-and-permissions"],
|
|
11712
|
+
["service-provider-mgmt", "service-provider"],
|
|
11713
|
+
["online-form-configuration", "online-forms"]
|
|
11714
|
+
].map((group) => Object.freeze(group))
|
|
11715
|
+
);
|
|
11716
|
+
function moduleSpellings(key) {
|
|
11717
|
+
return SPELLING_GROUPS.find((group) => group.includes(key)) ?? [key];
|
|
11718
|
+
}
|
|
11692
11719
|
function orgAllowsModule(modules, resource) {
|
|
11693
11720
|
if (!Array.isArray(modules) || modules.length === 0)
|
|
11694
11721
|
return true;
|
|
11695
11722
|
const key = typeof resource === "string" ? resource.trim() : "";
|
|
11696
11723
|
if (!key)
|
|
11697
11724
|
return true;
|
|
11698
|
-
|
|
11725
|
+
if (ALWAYS_ALLOWED.includes(key))
|
|
11726
|
+
return true;
|
|
11727
|
+
return moduleSpellings(key).some((spelling) => modules.includes(spelling));
|
|
11699
11728
|
}
|
|
11700
11729
|
|
|
11701
11730
|
// src/models/site.model.ts
|
|
@@ -11722,7 +11751,15 @@ function rejectedSiteModules(orgModules, requested) {
|
|
|
11722
11751
|
if (!narrows(orgModules))
|
|
11723
11752
|
return [];
|
|
11724
11753
|
const org = normaliseModules(orgModules);
|
|
11725
|
-
return normaliseModules(requested).filter((key) => !org
|
|
11754
|
+
return normaliseModules(requested).filter((key) => !orgAllowsModule(org, key));
|
|
11755
|
+
}
|
|
11756
|
+
function keepStoredSiteModules(metadata, stored) {
|
|
11757
|
+
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
|
|
11758
|
+
return metadata;
|
|
11759
|
+
}
|
|
11760
|
+
const { modules: _sent, ...rest } = metadata;
|
|
11761
|
+
const kept = stored && typeof stored === "object" && !Array.isArray(stored) ? stored.modules : void 0;
|
|
11762
|
+
return kept === void 0 ? rest : { ...rest, modules: kept };
|
|
11726
11763
|
}
|
|
11727
11764
|
function rejectedSiteModulesMessage(rejected) {
|
|
11728
11765
|
return `This organisation has not been given ${rejected.join(", ")}, so a site cannot offer it. Ask Seven365 to add it to the organisation first.`;
|
|
@@ -27949,9 +27986,12 @@ function useMemberController() {
|
|
|
27949
27986
|
}
|
|
27950
27987
|
const user = req.params.id;
|
|
27951
27988
|
const type = req.params.type;
|
|
27989
|
+
const org = typeof req.query.org === "string" && /^[0-9a-fA-F]{24}$/.test(req.query.org) ? req.query.org : "";
|
|
27952
27990
|
try {
|
|
27953
27991
|
await requireSelfOrPlatformStaff(req, user, { resource: "members", action: "view-members" });
|
|
27954
|
-
const
|
|
27992
|
+
const inOrg = org ? await _getByUserIdTypeOrg(user, type, org).catch(() => null) : null;
|
|
27993
|
+
const liveInOrg = inOrg && inOrg.status !== "deleted" && !inOrg.deletedAt ? inOrg : null;
|
|
27994
|
+
const data = liveInOrg ?? await _getByUserIdType(user, type);
|
|
27955
27995
|
res.json(data);
|
|
27956
27996
|
return;
|
|
27957
27997
|
} catch (error2) {
|
|
@@ -38724,8 +38764,8 @@ function useSiteController() {
|
|
|
38724
38764
|
}
|
|
38725
38765
|
const { _id, ...rest } = value;
|
|
38726
38766
|
await requireSiteWrite(req, _id);
|
|
38767
|
+
const existing = rest?.metadata ? await _getSiteById(_id) : null;
|
|
38727
38768
|
if (rest?.metadata?.residentAppModules) {
|
|
38728
|
-
const existing = await _getSiteById(_id);
|
|
38729
38769
|
const org = existing?.orgId ? await _getOrgById(existing.orgId.toString()).catch(
|
|
38730
38770
|
() => null
|
|
38731
38771
|
) : null;
|
|
@@ -38740,6 +38780,12 @@ function useSiteController() {
|
|
|
38740
38780
|
return;
|
|
38741
38781
|
}
|
|
38742
38782
|
}
|
|
38783
|
+
if (rest?.metadata) {
|
|
38784
|
+
rest.metadata = keepStoredSiteModules(
|
|
38785
|
+
rest.metadata,
|
|
38786
|
+
existing?.metadata
|
|
38787
|
+
);
|
|
38788
|
+
}
|
|
38743
38789
|
await _updateById(_id, rest);
|
|
38744
38790
|
res.json({ message: "Successfully updated site." });
|
|
38745
38791
|
return;
|