@7365admin1/core 3.47.1-staging.140 → 3.47.1-staging.141
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/organization-read-scope.md +44 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/test/e2e/harness.mjs +15 -0
- package/test/e2e/organization-read-scope.e2e.test.mjs +225 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/core": minor
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Scope the organisation lookups so they stop mapping one client to another.
|
|
6
|
+
|
|
7
|
+
Four reads on `/api/organizations` carried `requireAuth` and nothing more, and
|
|
8
|
+
every one took its key straight from the URL:
|
|
9
|
+
|
|
10
|
+
- `GET /api/organizations/user/:user` — which organisations ANY person belongs to
|
|
11
|
+
- `GET /api/organizations/orgs/:email` — the same, keyed on ANY email address
|
|
12
|
+
- `GET /api/organizations/name/:name` — any organisation, by name
|
|
13
|
+
- `GET /api/organizations/email/:email` — any organisation, by email address
|
|
14
|
+
|
|
15
|
+
Together those are a cross-client directory and an account-enumeration oracle: a
|
|
16
|
+
resident of one estate could map which clients an address or a user id belongs
|
|
17
|
+
to across the whole platform.
|
|
18
|
+
|
|
19
|
+
Each is now decided from the session:
|
|
20
|
+
|
|
21
|
+
- `user/:user` — the caller's own id, or Seven365 staff. Every live caller asks
|
|
22
|
+
about itself: the seven service-provider app layouts, the org app, and the
|
|
23
|
+
resident mobile app all pass the signed-in user's own id.
|
|
24
|
+
- `orgs/:email` — the caller's own address, compared case-insensitively after
|
|
25
|
+
being resolved from the session, or Seven365 staff. All three live callers
|
|
26
|
+
pass the signed-in user's own address.
|
|
27
|
+
- `name/:name` — Seven365 staff. Its only caller is the staff console's client
|
|
28
|
+
list.
|
|
29
|
+
- `email/:email` — Seven365 staff. No caller exists in any repository in the
|
|
30
|
+
organisation.
|
|
31
|
+
|
|
32
|
+
A key that does not exist is refused exactly as one that does, so the refusal
|
|
33
|
+
itself is not an oracle.
|
|
34
|
+
|
|
35
|
+
`GET /api/organizations/id/:id` is deliberately NOT guarded here: the same
|
|
36
|
+
controller function is mounted a second time, unauthenticated, at
|
|
37
|
+
`/api/organizations/org/id/:id`, which the sign-up-invite screens call before an
|
|
38
|
+
account exists. Guarding the handler would guard both mounts and break
|
|
39
|
+
registration. That one needs the alias split first.
|
|
40
|
+
|
|
41
|
+
`GET /api/organizations/` (the full client list) is also unchanged and is
|
|
42
|
+
recorded as a decision for the leads: it is reached from a live signup path that
|
|
43
|
+
must move onto the organisation `POST /api/organizations/onboarding` already
|
|
44
|
+
returns before the list can be staff-gated.
|
package/dist/index.js
CHANGED
|
@@ -21171,6 +21171,7 @@ async function recordConsoleAction(entry) {
|
|
|
21171
21171
|
// src/controllers/organization.controller.ts
|
|
21172
21172
|
function useOrgController() {
|
|
21173
21173
|
const { getOrgsByMembership } = useMemberRepo();
|
|
21174
|
+
const { getUserById: _getUserById } = useUserRepo();
|
|
21174
21175
|
const {
|
|
21175
21176
|
getByName: _getByName,
|
|
21176
21177
|
getById: _getById,
|
|
@@ -21298,6 +21299,9 @@ function useOrgController() {
|
|
|
21298
21299
|
const user = req.params.user;
|
|
21299
21300
|
const type = req.query.type ?? "";
|
|
21300
21301
|
try {
|
|
21302
|
+
if (user !== callerId(req)) {
|
|
21303
|
+
await requirePlatformStaff(req);
|
|
21304
|
+
}
|
|
21301
21305
|
const data = await getOrgsByMembership({
|
|
21302
21306
|
search,
|
|
21303
21307
|
page,
|
|
@@ -21323,6 +21327,7 @@ function useOrgController() {
|
|
|
21323
21327
|
return;
|
|
21324
21328
|
}
|
|
21325
21329
|
try {
|
|
21330
|
+
await requirePlatformStaff(req);
|
|
21326
21331
|
const data = await _getByName(name);
|
|
21327
21332
|
res.json(data);
|
|
21328
21333
|
return;
|
|
@@ -21360,6 +21365,7 @@ function useOrgController() {
|
|
|
21360
21365
|
return;
|
|
21361
21366
|
}
|
|
21362
21367
|
try {
|
|
21368
|
+
await requirePlatformStaff(req);
|
|
21363
21369
|
const data = await _getByEmail(email);
|
|
21364
21370
|
if (!data) {
|
|
21365
21371
|
next(new import_node_server_utils54.NotFoundError("Organization not found."));
|
|
@@ -21387,6 +21393,10 @@ function useOrgController() {
|
|
|
21387
21393
|
}
|
|
21388
21394
|
const email = req.params.email;
|
|
21389
21395
|
try {
|
|
21396
|
+
const self = await _getUserById(callerId(req)).catch(() => null);
|
|
21397
|
+
if (!self?.email || self.email.toLowerCase() !== email.toLowerCase()) {
|
|
21398
|
+
await requirePlatformStaff(req);
|
|
21399
|
+
}
|
|
21390
21400
|
const data = await _getOrgsByEmail(email);
|
|
21391
21401
|
res.json(data);
|
|
21392
21402
|
return;
|