@7365admin1/core 3.142.0 → 3.144.0
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/CHANGELOG.md +12 -0
- package/dist/index.d.ts +16 -1
- package/dist/index.js +570 -450
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +570 -452
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/e2e/staff-access.e2e.test.mjs +14 -1
- package/test/spaces-object.test.mjs +55 -0
- package/test/staff-camera-status.test.mjs +7 -8
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@7365admin1/core",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.144.0",
|
|
5
5
|
"author": "7365admin1",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.mjs",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"build": "tsup src/index.ts --format cjs,esm --dts && shx cp -r src/public dist && shx cp -r src/utils/handlebars dist",
|
|
14
14
|
"release": "yarn run build && changeset publish",
|
|
15
15
|
"lint": "tsc",
|
|
16
|
-
"test": "tsup src/config.ts src/utils/building-map.util.ts src/utils/robot-cloud.util.ts src/utils/robot-adapter.util.ts src/utils/png-encoder.util.ts src/services/demo.adapter.ts src/services/ounitech.adapter.ts src/utils/ounitech-codes.util.ts src/services/robot-cloud.service.ts src/services/robot-fleet.service.ts src/services/padbot.adapter.ts src/utils/padbot-sign.util.ts src/utils/padbot-mqtt.util.ts src/services/open-rmf.adapter.ts src/utils/robot-credentials.util.ts src/utils/rmf-building.util.ts src/utils/door-contract.util.ts src/utils/robot-fault.util.ts src/utils/robot-tracking.util.ts src/utils/robot-apps.util.ts src/utils/robot-permission.util.ts src/utils/robot-network.util.ts src/utils/lift-contract.util.ts src/models/robot-building-map.model.ts src/utils/anpr-revoke.util.ts src/utils/anpr-plate.util.ts src/utils/vehicle-request.util.ts src/utils/camera-view.util.ts src/utils/camera-bulk-import.util.ts src/utils/dashboard-metrics.util.ts src/utils/camera-capability.util.ts src/utils/camera-playback.util.ts src/services/camera-device-http.service.ts src/utils/camera-alert.util.ts src/utils/dahua-protocol.util.ts src/utils/site-name.util.ts src/utils/site-owner-edit.util.ts src/utils/service-provider-invite.util.ts src/utils/notification-category.util.ts src/utils/notification-kind.util.ts src/utils/notification-email.util.ts src/models/notification.model.ts src/utils/hid-card.util.ts src/utils/hid-json.util.ts src/utils/hid-access.util.ts src/models/customer-site.model.ts src/models/site-camera.model.ts src/models/site.model.ts src/models/member.model.ts src/models/subscription.model.ts src/utils/session-id.util.ts src/utils/occurrence-query.util.ts src/utils/occurrence-book-create.util.ts src/utils/update-result.util.ts src/utils/hid-amico-authz.util.ts src/utils/promo-code-currency.util.ts src/utils/console-audit.util.ts src/utils/org-suspension.util.ts src/utils/session-revoke.util.ts src/utils/member-suspension.util.ts src/models/hid-amico.model.ts src/models/patrol-email.model.ts src/utils/patrol-email-pdf.util.ts src/utils/visitor-status.util.ts src/utils/self-service-email.util.ts src/utils/hid-secret.util.ts src/models/visitor-invite.model.ts src/models/guard-invite.model.ts src/utils/site-timezone.util.ts src/utils/bulletin-service-apps.util.ts src/models/bulletin-board.model.ts src/utils/vehicle-import.util.ts src/utils/expired-vehicle-sweep.util.ts src/models/vehicle.model.ts src/controllers/chat.controller.ts src/utils/file-owner.util.ts src/utils/console-permission.util.ts src/utils/role-scope.util.ts src/utils/org-default-roles.util.ts src/utils/org-modules.util.ts src/utils/site-modules.util.ts src/utils/resident-app-modules.util.ts src/utils/resident-default-role.util.ts src/utils/invite-role-scope.util.ts src/models/personal-emergency-chain.model.ts src/utils/self-signup-files.util.ts src/models/person.model.ts src/utils/module-gate.util.ts src/utils/member-lookup.util.ts src/utils/notification-access.util.ts src/utils/module-access.util.ts src/services/module-gate.service.ts src/controllers/site.controller.ts src/controllers/organization.controller.ts src/controllers/member.controller.ts src/utils/org-owner-repair.util.ts src/utils/socket-session.util.ts src/utils/people-create-gate.util.ts src/utils/service-app-type.util.ts src/utils/customer-site-list.util.ts src/utils/org-search.util.ts src/utils/invite-origin.util.ts src/utils/member-role-assign.util.ts src/utils/cross-site-role.util.ts src/utils/cross-site-role-fix.util.ts src/utils/visitor-search.util.ts src/utils/attendance-proxy.util.ts src/models/attendance.model.ts src/services/attendance-photo.service.ts --format esm --out-dir test/.build --no-dts --silent && node --test test/*.test.mjs",
|
|
16
|
+
"test": "tsup src/config.ts src/utils/building-map.util.ts src/utils/robot-cloud.util.ts src/utils/robot-adapter.util.ts src/utils/png-encoder.util.ts src/services/demo.adapter.ts src/services/ounitech.adapter.ts src/utils/ounitech-codes.util.ts src/services/robot-cloud.service.ts src/services/robot-fleet.service.ts src/services/padbot.adapter.ts src/utils/padbot-sign.util.ts src/utils/padbot-mqtt.util.ts src/services/open-rmf.adapter.ts src/utils/robot-credentials.util.ts src/utils/rmf-building.util.ts src/utils/door-contract.util.ts src/utils/robot-fault.util.ts src/utils/robot-tracking.util.ts src/utils/robot-apps.util.ts src/utils/robot-permission.util.ts src/utils/robot-network.util.ts src/utils/lift-contract.util.ts src/models/robot-building-map.model.ts src/utils/anpr-revoke.util.ts src/utils/anpr-plate.util.ts src/utils/vehicle-request.util.ts src/utils/camera-view.util.ts src/utils/camera-bulk-import.util.ts src/utils/dashboard-metrics.util.ts src/utils/camera-capability.util.ts src/utils/camera-playback.util.ts src/services/camera-device-http.service.ts src/utils/camera-alert.util.ts src/utils/dahua-protocol.util.ts src/utils/site-name.util.ts src/utils/site-owner-edit.util.ts src/utils/service-provider-invite.util.ts src/utils/notification-category.util.ts src/utils/notification-kind.util.ts src/utils/notification-email.util.ts src/models/notification.model.ts src/utils/hid-card.util.ts src/utils/hid-json.util.ts src/utils/hid-access.util.ts src/models/customer-site.model.ts src/models/site-camera.model.ts src/models/site.model.ts src/models/member.model.ts src/models/subscription.model.ts src/utils/session-id.util.ts src/utils/occurrence-query.util.ts src/utils/occurrence-book-create.util.ts src/utils/update-result.util.ts src/utils/hid-amico-authz.util.ts src/utils/promo-code-currency.util.ts src/utils/console-audit.util.ts src/utils/org-suspension.util.ts src/utils/session-revoke.util.ts src/utils/member-suspension.util.ts src/models/hid-amico.model.ts src/models/patrol-email.model.ts src/utils/patrol-email-pdf.util.ts src/utils/visitor-status.util.ts src/utils/self-service-email.util.ts src/utils/hid-secret.util.ts src/models/visitor-invite.model.ts src/models/guard-invite.model.ts src/utils/site-timezone.util.ts src/utils/bulletin-service-apps.util.ts src/models/bulletin-board.model.ts src/utils/vehicle-import.util.ts src/utils/expired-vehicle-sweep.util.ts src/models/vehicle.model.ts src/controllers/chat.controller.ts src/utils/file-owner.util.ts src/utils/console-permission.util.ts src/utils/role-scope.util.ts src/utils/org-default-roles.util.ts src/utils/org-modules.util.ts src/utils/site-modules.util.ts src/utils/resident-app-modules.util.ts src/utils/resident-default-role.util.ts src/utils/invite-role-scope.util.ts src/models/personal-emergency-chain.model.ts src/utils/self-signup-files.util.ts src/models/person.model.ts src/utils/module-gate.util.ts src/utils/member-lookup.util.ts src/utils/notification-access.util.ts src/utils/module-access.util.ts src/services/module-gate.service.ts src/controllers/site.controller.ts src/controllers/organization.controller.ts src/controllers/member.controller.ts src/utils/org-owner-repair.util.ts src/utils/socket-session.util.ts src/utils/people-create-gate.util.ts src/utils/service-app-type.util.ts src/utils/customer-site-list.util.ts src/utils/org-search.util.ts src/utils/invite-origin.util.ts src/utils/member-role-assign.util.ts src/utils/cross-site-role.util.ts src/utils/cross-site-role-fix.util.ts src/utils/visitor-search.util.ts src/utils/attendance-proxy.util.ts src/models/attendance.model.ts src/services/attendance-photo.service.ts src/utils/spaces-object.util.ts --format esm --out-dir test/.build --no-dts --silent && node --test test/*.test.mjs",
|
|
17
17
|
"test:camera": "yarn test",
|
|
18
18
|
"test:e2e": "yarn build && tsup src/repositories/visitor-transaction.repo.ts --format esm --out-dir test/.build-e2e/repositories --no-dts --silent && node --test --test-concurrency=1 --test-timeout=600000 \"test/e2e/*.test.mjs\""
|
|
19
19
|
},
|
|
@@ -164,6 +164,19 @@ describe("Seven365 staff access", { concurrency: 1 }, () => {
|
|
|
164
164
|
assert.equal((await audit("staff-view.write")).length, before);
|
|
165
165
|
});
|
|
166
166
|
|
|
167
|
+
record("9b. inside a client, staff reach its cameras and vehicles like its owner; an outsider still cannot", async () => {
|
|
168
|
+
// Owner 2026-10-01 "no missed out": the camera wall (entitleSite) and every
|
|
169
|
+
// getUserPermissions(org) check (cameras, ANPR, vehicles) honour staff.
|
|
170
|
+
const { getSiteWall } = h.core.useCameraViewService();
|
|
171
|
+
const wall = await getSiteWall({ siteId: id.ownSite.toString(), userId: id.staff.toString() });
|
|
172
|
+
assert.ok(wall, "staff got the wall");
|
|
173
|
+
await assert.rejects(
|
|
174
|
+
getSiteWall({ siteId: id.ownSite.toString(), userId: id.otherOwner.toString() }),
|
|
175
|
+
/not found/i,
|
|
176
|
+
"another client's owner is still refused",
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
|
|
167
180
|
record("10. support@ preview lists every membership, named, with the cross-client site", async () => {
|
|
168
181
|
const res = await h.api(`${B}/memberships?email=${SUPPORT}`, { sid: staffSid });
|
|
169
182
|
assert.equal(res.status, 200, JSON.stringify(res.body));
|
|
@@ -264,5 +277,5 @@ async function seed(h) {
|
|
|
264
277
|
{ _id: supportClientMember, user: support, org: agency, siteId: strangerSite, siteName: "Stranger Condo", role: agencyRole, type: "security_agency", status: "active", createdBy: staff, createdAt: now },
|
|
265
278
|
]);
|
|
266
279
|
|
|
267
|
-
return { client, agency, ownSite, servedSite, strangerSite, staff, supportClientMember, supportStaffMember };
|
|
280
|
+
return { client, agency, ownSite, servedSite, strangerSite, staff, otherOwner: other, supportClientMember, supportStaffMember };
|
|
268
281
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
spacesUploadAcl,
|
|
6
|
+
isServableObjectKey,
|
|
7
|
+
anprSiteOfKey,
|
|
8
|
+
servedObjectHeaders,
|
|
9
|
+
} from "./.build/utils/spaces-object.util.mjs";
|
|
10
|
+
|
|
11
|
+
const ID = "68b0c1d2e3f4a5b6c7d8e9f0";
|
|
12
|
+
const SITE = "66aa11bb22cc33dd44ee55ff";
|
|
13
|
+
|
|
14
|
+
test("unset switch keeps every upload public-read (no behaviour change)", () => {
|
|
15
|
+
assert.equal(spacesUploadAcl(`default/${ID}`, ""), "public-read");
|
|
16
|
+
assert.equal(spacesUploadAcl(`anpr/${SITE}/${ID}`, ""), "public-read");
|
|
17
|
+
assert.equal(spacesUploadAcl(ID, ""), "public-read");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("a prefix makes only its own keys private", () => {
|
|
21
|
+
assert.equal(spacesUploadAcl(`anpr/${SITE}/${ID}`, "anpr/"), "private");
|
|
22
|
+
assert.equal(spacesUploadAcl(`default/${ID}`, "anpr/"), "public-read");
|
|
23
|
+
assert.equal(spacesUploadAcl(`default/${ID}`, " anpr/ , default/ "), "private");
|
|
24
|
+
assert.equal(spacesUploadAcl(ID, "*"), "private");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("only id-shaped keys are servable - no traversal, no logs, no listing", () => {
|
|
28
|
+
assert.ok(isServableObjectKey(`default/${ID}`));
|
|
29
|
+
assert.ok(isServableObjectKey(`anpr/${SITE}/${ID}`));
|
|
30
|
+
assert.ok(isServableObjectKey(ID));
|
|
31
|
+
for (const bad of ["", "default/", `../${ID}`, `default/../${ID}`, "production/logs/app.log",
|
|
32
|
+
`anpr/${SITE}/guard-post-1-2026-01-01-SBA1234A.jpeg`, `default//${ID}`, `default/${ID}.html`, null, 42]) {
|
|
33
|
+
assert.equal(isServableObjectKey(bad), false, String(bad));
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("an ANPR key names its site; others do not", () => {
|
|
38
|
+
assert.equal(anprSiteOfKey(`anpr/${SITE}/${ID}`), SITE);
|
|
39
|
+
assert.equal(anprSiteOfKey(`default/${ID}`), null);
|
|
40
|
+
assert.equal(anprSiteOfKey(`anpr/not-a-site/${ID}`), null);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("HTML and SVG are never shown inline on the app origin", () => {
|
|
44
|
+
for (const t of ["text/html", "image/svg+xml", "application/xhtml+xml", "text/javascript", "", undefined]) {
|
|
45
|
+
const h = servedObjectHeaders(t);
|
|
46
|
+
assert.equal(h["Content-Type"], "application/octet-stream", String(t));
|
|
47
|
+
assert.equal(h["Content-Disposition"], "attachment");
|
|
48
|
+
}
|
|
49
|
+
const img = servedObjectHeaders("image/jpeg");
|
|
50
|
+
assert.equal(img["Content-Type"], "image/jpeg");
|
|
51
|
+
assert.equal(img["Content-Disposition"], "inline");
|
|
52
|
+
assert.match(img["Content-Security-Policy"], /sandbox/);
|
|
53
|
+
assert.equal(img["X-Content-Type-Options"], "nosniff");
|
|
54
|
+
assert.equal(servedObjectHeaders("application/pdf; charset=binary")["Content-Type"], "application/pdf");
|
|
55
|
+
});
|
|
@@ -111,14 +111,13 @@ test("add and delete gained nothing - staff still cannot create or destroy a cam
|
|
|
111
111
|
}
|
|
112
112
|
});
|
|
113
113
|
|
|
114
|
-
test("staff
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
);
|
|
114
|
+
test("staff reach a client's cameras ONLY through the explicit staff rule (Open as staff, 2026-10-01)", () => {
|
|
115
|
+
// Owner decision 2026-10-01 ("no missed out"): Seven365 staff working inside
|
|
116
|
+
// a client see and set up its cameras like the owner. The only door is
|
|
117
|
+
// `staffReachesEveryClient` (live staff membership + cross-client tick), taken
|
|
118
|
+
// when no membership grants the site - never a bare isSuperAdmin.
|
|
119
|
+
assert.equal(/isSuperAdmin/.test(CAMERA_SERVICE), false, "a bare isSuperAdmin bypass appeared");
|
|
120
|
+
assert.match(CAMERA_SERVICE, /if \(!grant && \(await staffReachesEveryClient\(params\.userId\)\)\)/);
|
|
122
121
|
const list = handler("getAll");
|
|
123
122
|
assert.match(list, /entitleSite\(\{ siteId: site, userId: callerId\(req\) \}\)/);
|
|
124
123
|
assert.equal(/staffStatusChange|isSuperAdmin/.test(list), false);
|