@ministryofjustice/hmpps-prison-permissions-lib 1.1.2 → 2.0.1

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 CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  Please use this to capture reasoning behind changes:
4
4
 
5
+ # 2.0.1
6
+ Fixes incorrect usage of `previousPrisonId` and `previousPrisonLeavingDate` when checking contacts read permissions. These are only used in the context of transfers, not releases. The correct ones now in use are `lastPrisonId` and `releaseDate`.
7
+
8
+ # 2.0.0
9
+ Replaced contacts read permissions check with one that handles released prisoners differently. Checks for current prisoners are unaffected.
10
+
11
+ To be permitted to read contacts for released prisoners:
12
+ - the user's roles must include `InactiveBookings` and one or both of the following: `ContactsAdministrator, ContactsAuthoriser`
13
+ - the prisoner must have been released within the last 3 years (`PermissionCheckStatus.EXCEEDS_TIME_RESTRICTION`)
14
+ - the previous prison of the prisoner must match the user's caseload
15
+
16
+ These changes were requested by the contacts team after noticing the 'External Contacts' widget was not showing on the prisoner profiles of released prisoners. This was due to a caseload mismatch, as the assigned caseload for released prisoners is 'OUT'.
17
+
5
18
  ## 1.1.2
6
19
 
7
20
  Moved `@ministryofjustice/hmpps-npm-script-allowlist` to be a `dev` dependency as it’s used when working on this package
@@ -0,0 +1,4 @@
1
+ import { TestScenarios } from '../../../testUtils/TestScenario';
2
+ export declare const deniedContactsReadScenarios: TestScenarios;
3
+ export declare const grantedContactsReadScenarios: TestScenarios;
4
+ export declare const contactsReadCheckScenarios: TestScenarios;
@@ -0,0 +1,4 @@
1
+ import { TestScenarios } from '../../../../testUtils/TestScenario';
2
+ export declare const deniedContactsReadScenarios: TestScenarios;
3
+ export declare const grantedContactsReadScenarios: TestScenarios;
4
+ export declare const contactsReadCheckScenarios: TestScenarios;
@@ -5,4 +5,6 @@ export default interface Prisoner {
5
5
  supportingPrisonId?: string;
6
6
  previousPrisonId?: string;
7
7
  previousPrisonLeavingDate?: string;
8
+ lastPrisonId?: string;
9
+ releaseDate?: string;
8
10
  }
package/dist/index.cjs CHANGED
@@ -11,6 +11,7 @@ exports.PermissionCheckStatus = void 0;
11
11
  PermissionCheckStatus["RESTRICTED_PATIENT"] = "RESTRICTED_PATIENT";
12
12
  PermissionCheckStatus["PRISONER_IS_RELEASED"] = "PRISONER_IS_RELEASED";
13
13
  PermissionCheckStatus["PRISONER_IS_TRANSFERRING"] = "PRISONER_IS_TRANSFERRING";
14
+ PermissionCheckStatus["EXCEEDS_TIME_RESTRICTION"] = "EXCEEDS_TIME_RESTRICTION";
14
15
  PermissionCheckStatus["ROLE_NOT_PRESENT"] = "ROLE_NOT_PRESENT";
15
16
  PermissionCheckStatus["NOT_PRISON_USER"] = "NOT_PRISON_USER";
16
17
  PermissionCheckStatus["OK"] = "OK";
@@ -119,6 +120,11 @@ const isReleased = (prisoner) => {
119
120
  const isTransferring = (prisoner) => {
120
121
  return !!prisoner?.prisonId && ['TRN'].includes(prisoner.prisonId);
121
122
  };
123
+ const wasReleasedWithinThreeYears = (prisoner) => {
124
+ return (isReleased(prisoner) &&
125
+ !!prisoner.releaseDate &&
126
+ Date.parse(prisoner.releaseDate) > Date.now() - 3 * 365 * 24 * 60 * 60 * 1000);
127
+ };
122
128
  function userHasSomeRolesFrom(rolesToCheck, user) {
123
129
  return (rolesToCheck.length === 0 ||
124
130
  rolesToCheck.map(normaliseRoleText).some(role => user.userRoles.map(normaliseRoleText).includes(role)));
@@ -745,6 +751,21 @@ const inUsersCaseLoadAndUserHasSomeRolesFrom = (roles) => matchBaseCheckAnd({
745
751
 
746
752
  const inUsersCaseLoadAndUserHasRole = (role) => inUsersCaseLoadAndUserHasSomeRolesFrom([role]);
747
753
 
754
+ const contactsReadCheck = matchBaseCheckAnd({
755
+ overridingCondition: (user, prisoner) => {
756
+ if (isReleased(prisoner)) {
757
+ if (!userHasSomeRolesFrom([exports.Role.ContactsAdministrator, exports.Role.ContactsAuthoriser], user))
758
+ return exports.PermissionCheckStatus.ROLE_NOT_PRESENT;
759
+ if (!wasReleasedWithinThreeYears(prisoner))
760
+ return exports.PermissionCheckStatus.EXCEEDS_TIME_RESTRICTION;
761
+ if (!isInUsersCaseLoad(prisoner.lastPrisonId, user))
762
+ return exports.PermissionCheckStatus.NOT_IN_CASELOAD;
763
+ return exports.PermissionCheckStatus.OK;
764
+ }
765
+ return isInUsersCaseLoad(prisoner.prisonId, user) ? exports.PermissionCheckStatus.OK : exports.PermissionCheckStatus.NOT_IN_CASELOAD;
766
+ },
767
+ });
768
+
748
769
  function personalRelationshipsCheck(context) {
749
770
  const check = checkWith(context);
750
771
  return {
@@ -754,7 +775,7 @@ function personalRelationshipsCheck(context) {
754
775
  ...check(exports.PersonalRelationshipsPermission.edit_domestic_status, prisonerProfileEditCheck),
755
776
  ...check(exports.PersonalRelationshipsPermission.read_emergency_contacts, baseCheck),
756
777
  ...check(exports.PersonalRelationshipsPermission.edit_emergency_contacts, prisonerProfileSensitiveEditCheck),
757
- ...check(exports.PersonalRelationshipsPermission.read_contacts, inUsersCaseLoad),
778
+ ...check(exports.PersonalRelationshipsPermission.read_contacts, contactsReadCheck),
758
779
  ...check(exports.PersonalRelationshipsPermission.edit_contacts, inUsersCaseLoadAndUserHasSomeRolesFrom([exports.Role.ContactsAdministrator, exports.Role.ContactsAuthoriser])),
759
780
  ...check(exports.PersonalRelationshipsPermission.edit_contact_restrictions, inUsersCaseLoadAndUserHasRole(exports.Role.ContactsAuthoriser)),
760
781
  ...check(exports.PersonalRelationshipsPermission.edit_contact_visit_approval, inUsersCaseLoadAndUserHasRole(exports.Role.ContactsAuthoriser)),