@ministryofjustice/hmpps-prison-permissions-lib 0.2.1 → 0.3.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 CHANGED
@@ -2,6 +2,36 @@
2
2
 
3
3
  Please use this to capture reasoning behind changes:
4
4
 
5
+ ## 0.3.0
6
+
7
+ Official release for updates to the Case Notes and Religion permissions.
8
+
9
+ **Case Notes Permissions:**
10
+
11
+ Previously if a user had both the `POM` and `GLOBAL_SEARCH` roles they would be able read/write case notes any prisoner. The case notes permissions have now been updated so that users with both the `POM` and `GLOBAL_SEARCH` roles will only be able to read/write a prisoner's case notes if the prisoner has been in the users establishment within the last 30 days.
12
+
13
+ The permissions check requires additional data to be passed in via the `Prisoner` which now expects the `previousPrisonId` and `previousPrisonLeavingDate` fields to be present. This data has been added to the prisoner search API so clients using the permissions library will need to ensure the following fields are present in the `Prisoner` interface.
14
+
15
+ ```
16
+ export default interface Prisoner {
17
+ prisonerNumber: string
18
+ prisonId?: string
19
+ restrictedPatient: boolean
20
+ supportingPrisonId?: string
21
+ previousPrisonId?: string
22
+ previousPrisonLeavingDate?: string
23
+ }
24
+ ```
25
+
26
+ **Religion and Belief Permissions:**
27
+
28
+ The permissions check for `PersonProtectedCharacteristicsPermission.read_religion_and_belief` updated to only allow read access to a
29
+ prisoner's religion data to users who are part of the prisoner's caseload.
30
+
31
+ ## 0.3.0-alpha.1
32
+
33
+ Alpha release to test changes to the case notes and religion permissions.
34
+
5
35
  ## 0.2.0
6
36
 
7
37
  Incident response. We are introducing an additional role enabling staff to upload photos via DPS.
package/README.md CHANGED
@@ -149,6 +149,8 @@ setupNunjucksPermissions(njkEnv)
149
149
  {% endif %}
150
150
  ```
151
151
 
152
+ To mock permissions in your tests, follow [these instructions](readme/mocking.md).
153
+
152
154
  ## For library developers:
153
155
 
154
156
  1. [Publishing to NPM](readme/publishing.md)
@@ -1,2 +1,6 @@
1
1
  import { TestScenarios } from '../../../../../../testUtils/TestScenario';
2
+ export declare const deniedCaseNotesReadAndEditScenarios: TestScenarios;
3
+ export declare const grantedCaseNotesReadAndEditAfterTransferScenarios: TestScenarios;
4
+ export declare const grantedCaseNotesReadAndEditTransferringPrisonerScenarios: TestScenarios;
5
+ export declare const grantedCaseNotesReadAndEditScenarios: TestScenarios;
2
6
  export declare const caseNotesReadAndEditScenarios: TestScenarios;
@@ -0,0 +1,3 @@
1
+ import { TestScenarios } from '../../../../../../testUtils/TestScenario';
2
+ import { Role } from '../../../../../../types/internal/user/Role';
3
+ export declare function deniedSensitiveCaseNotesBaseScenarios(allPermissiveRoles: Role[]): TestScenarios;
@@ -1 +1,2 @@
1
- export declare const sensitiveCaseNotesDeleteScenarios: import("../../../../../../testUtils/TestScenario").TestScenarios;
1
+ import { TestScenarios } from '../../../../../../testUtils/TestScenario';
2
+ export declare const sensitiveCaseNotesDeleteScenarios: TestScenarios;
@@ -3,4 +3,6 @@ export default interface Prisoner {
3
3
  prisonId?: string;
4
4
  restrictedPatient: boolean;
5
5
  supportingPrisonId?: string;
6
+ previousPrisonId?: string;
7
+ previousPrisonLeavingDate?: string;
6
8
  }
package/dist/index.cjs CHANGED
@@ -521,6 +521,14 @@ exports.CaseNotesPermission = void 0;
521
521
  CaseNotesPermission["edit_sensitive"] = "prisoner:case-notes:sensitive:edit";
522
522
  })(exports.CaseNotesPermission || (exports.CaseNotesPermission = {}));
523
523
 
524
+ function daysToMilliseconds(days) {
525
+ return days * 24 * 60 * 60 * 1000;
526
+ }
527
+ function isDateWithinBounds(dateToCheckInMs, upperDateBoundInMs, lowerDateBoundInMs) {
528
+ return dateToCheckInMs <= upperDateBoundInMs && lowerDateBoundInMs <= dateToCheckInMs;
529
+ }
530
+
531
+ const caseNotesAccessPeriodPostTransferInMs = daysToMilliseconds(30);
524
532
  function caseNotesReadAndEditCheck(permission, request) {
525
533
  const baseCheckPassed = request.baseCheckStatus === exports.PermissionCheckStatus.OK;
526
534
  const caseNotesCheckStatus = checkCaseNotesAccess(request);
@@ -530,6 +538,13 @@ function caseNotesReadAndEditCheck(permission, request) {
530
538
  logDeniedPermissionCheck(permission, request, caseNotesCheckStatus);
531
539
  return check;
532
540
  }
541
+ function checkTimeBasedAccessToCaseNotesPostTransfer(user, prisoner, timePeriodForAccessPostTransferInMilliseconds) {
542
+ if (!isInUsersCaseLoad(prisoner.previousPrisonId, user) || !prisoner.previousPrisonLeavingDate)
543
+ return false;
544
+ const previousPrisonLeavingDate = Date.parse(prisoner.previousPrisonLeavingDate);
545
+ const today = Date.now();
546
+ return isDateWithinBounds(previousPrisonLeavingDate, today, today - timePeriodForAccessPostTransferInMilliseconds);
547
+ }
533
548
  function checkCaseNotesAccess(request) {
534
549
  const { user, prisoner } = request;
535
550
  // Restricted patients follows the base check rules:
@@ -547,10 +562,13 @@ function checkCaseNotesAccess(request) {
547
562
  // Case notes are accessible if the prisoner's prison is in the user's caseload:
548
563
  if (isInUsersCaseLoad(prisoner.prisonId, user))
549
564
  return exports.PermissionCheckStatus.OK;
550
- // Case notes for prisoners outside the user's caseload are only accessible with both Global Search and POM roles:
551
- return userHasAllRoles([exports.Role.GlobalSearch, exports.Role.PomUser], user)
565
+ if (!userHasAllRoles([exports.Role.GlobalSearch, exports.Role.PomUser], user))
566
+ return exports.PermissionCheckStatus.ROLE_NOT_PRESENT;
567
+ // Case notes for prisoners outside the user's caseload are only accessible with both Global Search and POM roles if the prisoner
568
+ // was previously in one of the users caseloads in the 30 days:
569
+ return checkTimeBasedAccessToCaseNotesPostTransfer(user, prisoner, caseNotesAccessPeriodPostTransferInMs)
552
570
  ? exports.PermissionCheckStatus.OK
553
- : exports.PermissionCheckStatus.ROLE_NOT_PRESENT;
571
+ : exports.PermissionCheckStatus.NOT_PERMITTED;
554
572
  }
555
573
 
556
574
  const permission$7 = exports.CaseNotesPermission.read;
@@ -558,10 +576,10 @@ function caseNotesReadCheck(request) {
558
576
  return caseNotesReadAndEditCheck(permission$7, request);
559
577
  }
560
578
 
561
- function baseCheckAndUserHasSomeRolesFrom(roles, permission, request) {
562
- const { user, baseCheckStatus } = request;
563
- const baseCheckPassed = baseCheckStatus === exports.PermissionCheckStatus.OK;
564
- const check = baseCheckPassed && userHasSomeRolesFrom(roles, user);
579
+ function caseNotesReadAndEditCheckAndUserHasRolesFrom(roles, permission, request) {
580
+ const { user } = request;
581
+ const baseCaseNotesCheckPassed = caseNotesReadAndEditCheck(permission, request);
582
+ const check = baseCaseNotesCheckPassed && userHasSomeRolesFrom(roles, user);
565
583
  if (!check)
566
584
  logDeniedPermissionCheck(permission, request, exports.PermissionCheckStatus.ROLE_NOT_PRESENT);
567
585
  return check;
@@ -569,17 +587,17 @@ function baseCheckAndUserHasSomeRolesFrom(roles, permission, request) {
569
587
 
570
588
  const permission$6 = exports.CaseNotesPermission.read_sensitive;
571
589
  function sensitiveCaseNotesReadCheck(request) {
572
- return baseCheckAndUserHasSomeRolesFrom([exports.Role.PomUser, exports.Role.ViewSensitiveCaseNotes, exports.Role.AddSensitiveCaseNotes], permission$6, request);
590
+ return caseNotesReadAndEditCheckAndUserHasRolesFrom([exports.Role.PomUser, exports.Role.ViewSensitiveCaseNotes, exports.Role.AddSensitiveCaseNotes], permission$6, request);
573
591
  }
574
592
 
575
593
  const permission$5 = exports.CaseNotesPermission.delete_sensitive;
576
594
  function sensitiveCaseNotesDeleteCheck(request) {
577
- return baseCheckAndUserHasRole(exports.Role.DeleteSensitiveCaseNotes, permission$5, request);
595
+ return caseNotesReadAndEditCheckAndUserHasRolesFrom([exports.Role.DeleteSensitiveCaseNotes], permission$5, request);
578
596
  }
579
597
 
580
598
  const permission$4 = exports.CaseNotesPermission.edit_sensitive;
581
599
  function sensitiveCaseNotesEditCheck(request) {
582
- return baseCheckAndUserHasSomeRolesFrom([exports.Role.PomUser, exports.Role.AddSensitiveCaseNotes], permission$4, request);
600
+ return caseNotesReadAndEditCheckAndUserHasRolesFrom([exports.Role.PomUser, exports.Role.AddSensitiveCaseNotes], permission$4, request);
583
601
  }
584
602
 
585
603
  const permission$3 = exports.CaseNotesPermission.edit;
@@ -735,7 +753,7 @@ function personProtectedCharacteristicsCheck(request) {
735
753
  return {
736
754
  ...readCheck$3(exports.PersonProtectedCharacteristicsPermission.read_sexual_orientation, request),
737
755
  ...sensitiveEditCheck$1(exports.PersonProtectedCharacteristicsPermission.edit_sexual_orientation, request),
738
- ...readCheck$3(exports.PersonProtectedCharacteristicsPermission.read_religion_and_belief, request),
756
+ [exports.PersonProtectedCharacteristicsPermission.read_religion_and_belief]: inUsersCaseLoad(exports.PersonProtectedCharacteristicsPermission.read_religion_and_belief, request),
739
757
  ...editCheck$3(exports.PersonProtectedCharacteristicsPermission.edit_religion_and_belief, request),
740
758
  ...readCheck$3(exports.PersonProtectedCharacteristicsPermission.read_ethnicity, request),
741
759
  ...sensitiveEditCheck$1(exports.PersonProtectedCharacteristicsPermission.edit_ethnicity, request),
@@ -850,6 +868,15 @@ function personCheck(request) {
850
868
  };
851
869
  }
852
870
 
871
+ function baseCheckAndUserHasSomeRolesFrom(roles, permission, request) {
872
+ const { user, baseCheckStatus } = request;
873
+ const baseCheckPassed = baseCheckStatus === exports.PermissionCheckStatus.OK;
874
+ const check = baseCheckPassed && userHasSomeRolesFrom(roles, user);
875
+ if (!check)
876
+ logDeniedPermissionCheck(permission, request, exports.PermissionCheckStatus.ROLE_NOT_PRESENT);
877
+ return check;
878
+ }
879
+
853
880
  exports.PathfinderPermission = void 0;
854
881
  (function (PathfinderPermission) {
855
882
  PathfinderPermission["read"] = "prisoner:pathfinder:read";