@7365admin1/core 3.72.1-staging.325 → 3.72.1-staging.326
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/dist/index.d.ts +3 -1
- package/dist/index.js +113 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +113 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/scripts/hid-backfill-enrolment-access.mjs +198 -0
- package/test/hid-enrolment-grants-access.test.mjs +162 -0
package/dist/index.d.ts
CHANGED
|
@@ -13592,7 +13592,9 @@ declare function useHidAmicoService(): {
|
|
|
13592
13592
|
hasMore: boolean;
|
|
13593
13593
|
pageRange: string;
|
|
13594
13594
|
}>;
|
|
13595
|
-
createIdentity: (readerId: string, value: Partial<THidAmicoIdentity>
|
|
13595
|
+
createIdentity: (readerId: string, value: Partial<THidAmicoIdentity> & {
|
|
13596
|
+
orgId?: string;
|
|
13597
|
+
}) => Promise<{
|
|
13596
13598
|
_id: bson.ObjectId;
|
|
13597
13599
|
reader: bson.ObjectId;
|
|
13598
13600
|
site: bson.ObjectId;
|
package/dist/index.js
CHANGED
|
@@ -44051,6 +44051,15 @@ var schemaHidAmicoIdentity = import_joi66.default.object({
|
|
|
44051
44051
|
}).or("hidUserId", "registration", "cardNo");
|
|
44052
44052
|
var schemaCreateHidAmicoIdentity = import_joi66.default.object({
|
|
44053
44053
|
site: objectIdSchema2.optional(),
|
|
44054
|
+
/*
|
|
44055
|
+
* Which organisation's HID permission document this enrolment's access grant
|
|
44056
|
+
* belongs in. Optional so existing callers keep working - the service falls
|
|
44057
|
+
* back to the site's own organisation - but a managing agent should send its
|
|
44058
|
+
* own org, because a site reached through `customer.sites` would otherwise
|
|
44059
|
+
* get a second document and the permissions screen would show the person as
|
|
44060
|
+
* unassigned while the door let them through.
|
|
44061
|
+
*/
|
|
44062
|
+
orgId: objectIdSchema2.optional(),
|
|
44054
44063
|
hidUserId: import_joi66.default.alternatives(import_joi66.default.string(), import_joi66.default.number()).optional().allow(null, ""),
|
|
44055
44064
|
registration: import_joi66.default.string().trim().optional().allow(""),
|
|
44056
44065
|
cardNo: import_joi66.default.string().optional().allow(null, ""),
|
|
@@ -45811,6 +45820,27 @@ function resolveHidPhysicalCardValue(input) {
|
|
|
45811
45820
|
return normalizeHidCardValue(input.cardValue);
|
|
45812
45821
|
}
|
|
45813
45822
|
|
|
45823
|
+
// src/utils/hid-assignment.util.ts
|
|
45824
|
+
function permissionSubjectOfIdentity(value) {
|
|
45825
|
+
if (value.person) {
|
|
45826
|
+
return { subjectId: String(value.person), category: "resident" };
|
|
45827
|
+
}
|
|
45828
|
+
if (value.member) {
|
|
45829
|
+
return { subjectId: String(value.member), category: "property_management" };
|
|
45830
|
+
}
|
|
45831
|
+
if (value.serviceProvider) {
|
|
45832
|
+
return { subjectId: String(value.serviceProvider), category: "service_provider" };
|
|
45833
|
+
}
|
|
45834
|
+
return null;
|
|
45835
|
+
}
|
|
45836
|
+
function assignmentNamesSubject(item, subject, scope) {
|
|
45837
|
+
if (String(item.subjectId ?? "") !== subject.subjectId)
|
|
45838
|
+
return false;
|
|
45839
|
+
if (item.category !== subject.category)
|
|
45840
|
+
return false;
|
|
45841
|
+
return item.readerId ? String(item.readerId) === scope.readerId : item.locationKey === scope.locationKey;
|
|
45842
|
+
}
|
|
45843
|
+
|
|
45814
45844
|
// src/utils/hid-json.util.ts
|
|
45815
45845
|
var import_node_crypto5 = __toESM(require("crypto"));
|
|
45816
45846
|
var UINT64_MAX = 2n ** 64n - 1n;
|
|
@@ -48584,6 +48614,71 @@ function useHidAmicoService() {
|
|
|
48584
48614
|
await client.logout();
|
|
48585
48615
|
}
|
|
48586
48616
|
}
|
|
48617
|
+
async function resolvePermissionOrgId(reader, requested) {
|
|
48618
|
+
if (requested)
|
|
48619
|
+
return String(requested);
|
|
48620
|
+
const site = await siteRepo.getSiteById(String(reader.site));
|
|
48621
|
+
const orgId = String(site?.org ?? "");
|
|
48622
|
+
if (!orgId) {
|
|
48623
|
+
throw new import_node_server_utils141.BadRequestError("This site has no organisation, so HID access cannot be granted.");
|
|
48624
|
+
}
|
|
48625
|
+
return orgId;
|
|
48626
|
+
}
|
|
48627
|
+
async function addEnrolmentAssignment(reader, orgId, subject) {
|
|
48628
|
+
const siteId = String(reader.site);
|
|
48629
|
+
const readerId = String(reader._id);
|
|
48630
|
+
const scope = await getPermissionScope(orgId, siteId, readerId);
|
|
48631
|
+
const current = await repo.getSitePermissions(orgId, siteId);
|
|
48632
|
+
const assignments = current.assignments ?? [];
|
|
48633
|
+
if (assignments.some((item) => assignmentNamesSubject(item, subject, scope))) {
|
|
48634
|
+
return null;
|
|
48635
|
+
}
|
|
48636
|
+
const [resolved] = await repo.resolvePermissionAssignments(siteId, [
|
|
48637
|
+
{ ...subject, intercom: false }
|
|
48638
|
+
]);
|
|
48639
|
+
if (!resolved) {
|
|
48640
|
+
throw new import_node_server_utils141.BadRequestError("This person cannot be granted access at this site.");
|
|
48641
|
+
}
|
|
48642
|
+
const next = [...assignments, {
|
|
48643
|
+
...resolved,
|
|
48644
|
+
readerId: scope.readerId,
|
|
48645
|
+
location: scope.location,
|
|
48646
|
+
locationKey: scope.locationKey
|
|
48647
|
+
}];
|
|
48648
|
+
await repo.saveSitePermissions(orgId, siteId, next);
|
|
48649
|
+
return async () => {
|
|
48650
|
+
await repo.saveSitePermissions(orgId, siteId, assignments);
|
|
48651
|
+
};
|
|
48652
|
+
}
|
|
48653
|
+
async function removeEnrolmentAssignment(reader, subject) {
|
|
48654
|
+
const siteId = String(reader.site);
|
|
48655
|
+
const readerId = String(reader._id);
|
|
48656
|
+
const scope = {
|
|
48657
|
+
readerId,
|
|
48658
|
+
locationKey: normalizePermissionLocation(String(reader.location ?? ""))
|
|
48659
|
+
};
|
|
48660
|
+
const documents = await repo.listSitePermissionDocuments(siteId);
|
|
48661
|
+
const undos = [];
|
|
48662
|
+
for (const document2 of documents) {
|
|
48663
|
+
const assignments = document2.assignments ?? [];
|
|
48664
|
+
const next = assignments.filter((item) => !assignmentNamesSubject(item, subject, scope));
|
|
48665
|
+
if (next.length === assignments.length)
|
|
48666
|
+
continue;
|
|
48667
|
+
const orgId = String(document2.orgId ?? "");
|
|
48668
|
+
if (!orgId)
|
|
48669
|
+
continue;
|
|
48670
|
+
await repo.saveSitePermissions(orgId, siteId, next);
|
|
48671
|
+
undos.push(async () => {
|
|
48672
|
+
await repo.saveSitePermissions(orgId, siteId, assignments);
|
|
48673
|
+
});
|
|
48674
|
+
}
|
|
48675
|
+
if (!undos.length)
|
|
48676
|
+
return null;
|
|
48677
|
+
return async () => {
|
|
48678
|
+
for (const undo of undos)
|
|
48679
|
+
await undo();
|
|
48680
|
+
};
|
|
48681
|
+
}
|
|
48587
48682
|
async function resolveSubjectUser(site, value) {
|
|
48588
48683
|
const subject = value.person ? { subjectId: String(value.person), category: "resident" } : value.member ? { subjectId: String(value.member), category: "property_management" } : value.serviceProvider ? { subjectId: String(value.serviceProvider), category: "service_provider" } : null;
|
|
48589
48684
|
if (!subject) {
|
|
@@ -48615,6 +48710,7 @@ function useHidAmicoService() {
|
|
|
48615
48710
|
);
|
|
48616
48711
|
}
|
|
48617
48712
|
async function createIdentity(readerId, value) {
|
|
48713
|
+
const { orgId: requestedOrgId, ...identityValue } = value;
|
|
48618
48714
|
const reader = await repo.getById(readerId);
|
|
48619
48715
|
const linkedSubjects = [value.person, value.member, value.serviceProvider, value.visitor].filter(Boolean);
|
|
48620
48716
|
if (linkedSubjects.length !== 1) {
|
|
@@ -48635,7 +48731,7 @@ function useHidAmicoService() {
|
|
|
48635
48731
|
const subjectUser = await resolveSubjectUser(String(reader.site), value);
|
|
48636
48732
|
await assertSubjectNotEnrolled(readerId, value, subjectUser);
|
|
48637
48733
|
const identity = await repo.addIdentity({
|
|
48638
|
-
...
|
|
48734
|
+
...identityValue,
|
|
48639
48735
|
// Stored, not derived on read. It is how this row is recognised as the
|
|
48640
48736
|
// same human as one written by a different screen, and a `site.people`
|
|
48641
48737
|
// row can be re-pointed at another account after the fact.
|
|
@@ -48643,9 +48739,17 @@ function useHidAmicoService() {
|
|
|
48643
48739
|
reader: readerId,
|
|
48644
48740
|
site: value.site || reader?.site?.toString()
|
|
48645
48741
|
});
|
|
48742
|
+
let undoAssignment = null;
|
|
48646
48743
|
try {
|
|
48744
|
+
const subject = permissionSubjectOfIdentity(identityValue);
|
|
48745
|
+
if (subject) {
|
|
48746
|
+
const orgId = await resolvePermissionOrgId(reader, requestedOrgId);
|
|
48747
|
+
undoAssignment = await addEnrolmentAssignment(reader, orgId, subject);
|
|
48748
|
+
}
|
|
48647
48749
|
await reconcileStoredReaderPermissions(readerId);
|
|
48648
48750
|
} catch (error) {
|
|
48751
|
+
if (undoAssignment)
|
|
48752
|
+
await undoAssignment();
|
|
48649
48753
|
await repo.deleteIdentity(identity._id);
|
|
48650
48754
|
throw error;
|
|
48651
48755
|
}
|
|
@@ -48706,9 +48810,17 @@ function useHidAmicoService() {
|
|
|
48706
48810
|
async function deleteIdentity(identityId) {
|
|
48707
48811
|
const current = await repo.getIdentityById(identityId);
|
|
48708
48812
|
const identity = await repo.deleteIdentity(identityId);
|
|
48813
|
+
let undoAssignment = null;
|
|
48709
48814
|
try {
|
|
48815
|
+
const subject = permissionSubjectOfIdentity(current);
|
|
48816
|
+
if (subject) {
|
|
48817
|
+
const reader = await repo.getById(String(current.reader));
|
|
48818
|
+
undoAssignment = await removeEnrolmentAssignment(reader, subject);
|
|
48819
|
+
}
|
|
48710
48820
|
await reconcileStoredReaderPermissions(String(current.reader));
|
|
48711
48821
|
} catch (error) {
|
|
48822
|
+
if (undoAssignment)
|
|
48823
|
+
await undoAssignment();
|
|
48712
48824
|
await repo.updateIdentity(identityId, { status: current.status || "active", deletedAt: "" });
|
|
48713
48825
|
throw error;
|
|
48714
48826
|
}
|