Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restricted case access #3193

Merged
merged 53 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
3986921
Package update
DavidJayakumar Aug 2, 2023
209c572
node Api changes
DavidJayakumar Aug 2, 2023
dfc0806
front end changes
DavidJayakumar Aug 2, 2023
c8ccc91
fix lint
DavidJayakumar Aug 2, 2023
d5d9d6d
Add pact
DavidJayakumar Aug 2, 2023
3b6c7de
fix lint
DavidJayakumar Aug 2, 2023
9591853
Add translatioin (#3194)
DavidJayakumar Aug 3, 2023
061e194
Fix audit issue
DavidJayakumar Aug 4, 2023
c707236
Merge branch 'master' into feature/EUI-6646
johnbenjamin-hmcts Aug 8, 2023
697351e
Merge branch 'master' into feature/EUI-6646
johnbenjamin-hmcts Aug 11, 2023
0daf69e
Merge branch 'master' into feature/EUI-6646
johnbenjamin-hmcts Aug 14, 2023
cd9afcd
Merge branch 'master' into feature/EUI-6646
johnbenjamin-hmcts Aug 16, 2023
e2a3fe7
Merge branch 'master' into feature/EUI-6646
johnbenjamin-hmcts Aug 16, 2023
ced9a12
Merge from master
johnbenjamin-hmcts Aug 16, 2023
86e5c4d
Merge branch 'master' into feature/EUI-6646
johnbenjamin-hmcts Aug 17, 2023
c7926a3
Merge branch 'master' into feature/EUI-6646
johnbenjamin-hmcts Aug 30, 2023
6537063
feature/EUI-6646: Setting Preview Deployment ID
johnbenjamin-hmcts Aug 30, 2023
38ee423
Merge branch 'master' into feature/EUI-6646
johnbenjamin-hmcts Sep 5, 2023
d28a38d
Update after merge from master
johnbenjamin-hmcts Sep 5, 2023
4942fb0
Merge branch 'master' into feature/EUI-6646
johnbenjamin-hmcts Sep 19, 2023
fb2e7d0
Merge from master
johnbenjamin-hmcts Sep 19, 2023
2821faf
Merge branch 'master' into feature/EUI-6646
johnbenjamin-hmcts Sep 28, 2023
748b6e7
Update yarn-audit-known-issues
johnbenjamin-hmcts Sep 28, 2023
616078d
Update package.json
johnbenjamin-hmcts Sep 28, 2023
a0ff086
Merge branch 'master' into feature/EUI-6646
johnbenjamin-hmcts Sep 29, 2023
3d63bb3
Merge from master
johnbenjamin-hmcts Sep 29, 2023
9e052ac
Merge branch 'master' into feature/EUI-6646
johnbenjamin-hmcts Oct 17, 2023
858ea8e
EUI-8816 Restricted Case Access - Feature toggle for deployment to Pr…
johnbenjamin-hmcts Oct 17, 2023
d8d9bbf
EUI-8816 Restricted Case Access - Feature toggle for deployment to Pr…
johnbenjamin-hmcts Oct 17, 2023
bba1f0f
EUI-8816 Restricted Case Access - Feature toggle for deployment to Pr…
johnbenjamin-hmcts Oct 17, 2023
b20e711
Update yarn-audit-known-issues
johnbenjamin-hmcts Oct 17, 2023
19b4dd5
Merge branch 'master' into feature/EUI-6646
johnbenjamin-hmcts Oct 27, 2023
e393754
Merge branch 'feature/EUI-6646' into feature/EUI-8816-Restricted-Case…
johnbenjamin-hmcts Oct 27, 2023
3dd0774
EUI-8816 Restricted Case Access - Feature toggle for deployment to Pr…
johnbenjamin-hmcts Oct 30, 2023
acdaa19
restriced case access, functional tests added
sreekanthpuligadda Nov 2, 2023
733d14e
Merge branch 'master' into feature/EUI-8816-Restricted-Case-Access---…
johnbenjamin-hmcts Nov 7, 2023
bc78611
EUI-8816 Restricted Case Access - Feature toggle for deployment to Pr…
johnbenjamin-hmcts Nov 8, 2023
722e2e2
Merge branch 'master' into feature/EUI-6646
johnbenjamin-hmcts Nov 8, 2023
b282b00
Merge branch 'feature/EUI-6646' into feature/EUI-8816-Restricted-Case…
johnbenjamin-hmcts Nov 8, 2023
330f965
Update toolkit version
johnbenjamin-hmcts Nov 8, 2023
ae5b654
EUI-8816 Restricted Case Access - Feature toggle for deployment to Pr…
johnbenjamin-hmcts Nov 9, 2023
4837d4d
Merge branch 'master' into feature/EUI-6646
DavidJayakumar Nov 21, 2023
5723f59
fix seruity issue
DavidJayakumar Nov 21, 2023
d255c8b
restricted case test updates
sreekanthpuligadda Nov 24, 2023
5a9af81
Merge branch 'feature/EUI-6646' into feature/EUI-8816-Restricted-Case…
johnbenjamin-hmcts Nov 24, 2023
d35f88b
Update toolkit version
johnbenjamin-hmcts Nov 24, 2023
e74b086
Merge pull request #3329 from hmcts/feature/EUI-8816-Restricted-Case-…
johnbenjamin-hmcts Nov 24, 2023
153186a
Merge branch 'master' into feature/EUI-6646
DavidJayakumar Nov 29, 2023
6324554
multi interaction pact setup required, to be fixed separately
sreekanthpuligadda Nov 29, 2023
687115d
Update toolkit version
johnbenjamin-hmcts Nov 30, 2023
f50dd1e
Update yarn-audit-known-issues
johnbenjamin-hmcts Nov 30, 2023
411d781
Merge branch 'master' into feature/EUI-6646
johnbenjamin-hmcts Nov 30, 2023
398fe36
Update toolkit version
johnbenjamin-hmcts Nov 30, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions api/roleAccess/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import {
import { getEmail, getJudicialUsersFromApi, getUserName, mapRoleCategory } from './exclusionService';
import { CaseRoleRequestPayload } from './models/caseRoleRequestPayload';
import { release2ContentType } from './models/release2ContentType';
import { getSubstantiveRoles } from './roleAssignmentService';
import { Role } from './models/roleType';
import { getAllRoles, getSubstantiveRoles } from './roleAssignmentService';

const baseRoleAccessUrl = getConfigValue(SERVICES_ROLE_ASSIGNMENT_API_PATH);
const SUPPORTED_ROLE_CATEGORIES = ['LEGAL_OPERATIONS', 'JUDICIAL', 'CTSC', 'ADMIN'];
Expand Down Expand Up @@ -55,7 +56,7 @@ export async function getRolesByCaseId(req: EnhancedRequest, res: Response, next
}
}

export async function getAccessRolesByCaseId(req: EnhancedRequest, res: Response, next: NextFunction): Promise<Response> {
export async function getAccessRoles(req: EnhancedRequest, res: Response, next: NextFunction): Promise<Response> {
const requestPayload = getAccessRolesRequestPayload(req.body.caseId, req.body.jurisdiction, req.body.caseType);
const basePath = getConfigValue(SERVICES_ROLE_ASSIGNMENT_API_PATH);
const fullPath = `${basePath}/am/role-assignments/query`;
Expand All @@ -73,6 +74,34 @@ export async function getAccessRolesByCaseId(req: EnhancedRequest, res: Response
}
}

export async function getAccessRolesByCaseId(req: EnhancedRequest, res: Response, next: NextFunction): Promise<Response> {
const requestPayload = getAccessRolesRequestPayloadForCaseId(req.body.caseId);
const basePath = getConfigValue(SERVICES_ROLE_ASSIGNMENT_API_PATH);
const fullPath = `${basePath}/am/role-assignments/query`;
const headers = setHeaders(req, release2ContentType);
try {
const response: AxiosResponse = await http.post(fullPath, requestPayload, { headers });
const finalRoles: CaseRole[] = mapResponseToCaseRoles(
response.data.roleAssignmentResponse,
req.body.assignmentId,
req
);
if (finalRoles) {
const rolesResponse = await getAllRoles(req);
const roles = (rolesResponse.data as Role[]);
finalRoles?.forEach((finalRole) => {
const role = roles?.find((role) => role.name === finalRole.roleName);
if (role) {
finalRole.roleName = role.label;
}
});
}
return res.status(response.status).send(finalRoles);
} catch (error) {
next(error);
}
}

export async function getJudicialUsers(req: EnhancedRequest, res: Response, next: NextFunction): Promise<Response> {
const userIds = req.body.userIds;
// Ensures there are no errors when no userIds are provided
Expand Down Expand Up @@ -330,6 +359,18 @@ export function getAccessRolesRequestPayload(caseId: string,
};
}

export function getAccessRolesRequestPayloadForCaseId(caseId: string): CaseRoleRequestPayload {
return {
queryRequests: [
{
attributes: {
caseId: [caseId]
}
}
]
};
}

// instances of specific reason appearing as JSON from toolkit versions - this enables both possibilities
export function getSpecificReason(note: string): string {
if (note.charAt(0) !== '{') {
Expand Down
4 changes: 3 additions & 1 deletion api/roleAccess/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
confirmAllocateRole,
createSpecificAccessApprovalRole,
deleteRoleByCaseAndRoleId,
getAccessRoles,
getAccessRolesByCaseId,
getJudicialUsers,
getMyAccessNewCount,
Expand All @@ -27,7 +28,8 @@ router.post('/allocate-role/delete', deleteRoleByCaseAndRoleId);

router.post('/allocate-role/valid-roles', getPossibleRoles);
router.post('/roles/post', getRolesByCaseId);
router.post('/roles/access-get', getAccessRolesByCaseId);
router.post('/roles/access-get', getAccessRoles);
router.post('/roles/access-get-by-caseId', getAccessRolesByCaseId);
router.post('/roles/getJudicialUsers', getJudicialUsers);

router.get('/roles/get-my-access-new-count', getMyAccessNewCount);
Expand Down
143 changes: 143 additions & 0 deletions api/test/pact/pact-tests/rolesAccess/getAccessRolesByCaseId.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { expect } from 'chai';
import * as config from 'config';
import * as sinon from 'sinon';
import { mockReq, mockRes } from 'sinon-express-mock';
import { PactTestSetup } from '../settings/provider.mock';
import { getAccessManagementServiceAPIOverrides } from '../utils/configOverride';
import { requireReloaded } from '../utils/moduleUtil';

const { Matchers } = require('@pact-foundation/pact');
const { somethingLike } = Matchers;
const pactSetUp = new PactTestSetup({ provider: 'am_roleAssignment_queryAssignment', port: 8000 });

const caseId = '12345';

describe.skip('getAccessRolesByCaseId - access management service, query role assignments', () => {
const REQUEST_BODY = {
queryRequests: [
{
attributes: {
caseId: [somethingLike('12345')]
}
}
]
};
const RESPONSE_BODY = {
roleAssignmentResponse: [
{
id: somethingLike('b83acc2f-6720-4cf9-a1f8-52367c35963d'),
actorIdType: somethingLike('IDAM'),
actorId: somethingLike('271ebdd4-f757-492d-b57f-101b8e47c90e'),
roleType: somethingLike('CASE'),
roleName: somethingLike('case-manager'),
classification: somethingLike('PUBLIC'),
grantType: somethingLike('SPECIFIC'),
roleCategory: somethingLike('LEGAL_OPERATIONS'),
readOnly: somethingLike(false),
beginTime: somethingLike('2022-09-15T23:00:00Z'),
created: somethingLike('2022-09-16T13:06:44.295367Z'),
attributes: {
substantive: somethingLike('Y'),
caseId: somethingLike('1546883526751282'),
jurisdiction: somethingLike('IA'),
caseType: somethingLike('Asylum')
}
}
]
};

describe('post /am/role-assignments/query', () => {
const sandbox: sinon.SinonSandbox = sinon.createSandbox();
let next;

beforeEach(() => {
next = sandbox.spy();
});

before(async () => {
await pactSetUp.provider.setup();
const interaction = {
state: 'A list of role assignments for the search query',
uponReceiving: 'query role assignments for caseId',
withRequest: {
method: 'POST',
path: '/am/role-assignments/query',
headers: {
'Authorization': 'Bearer someAuthorizationToken',
'ServiceAuthorization': 'Bearer someServiceAuthorizationToken',
'content-type': 'application/vnd.uk.gov.hmcts.role-assignment-service.post-assignment-query-request+json;charset=UTF-8;version=2.0'
},
body: REQUEST_BODY
},
willRespondWith: {
status: 200,
headers: {
'content-type': 'application/vnd.uk.gov.hmcts.role-assignment-service.post-assignment-query-request+json;charset=UTF-8;version=2.0'
},
body: RESPONSE_BODY
}
};
// @ts-ignore
pactSetUp.provider.addInteraction(interaction);
});

afterEach(() => {
sandbox.restore();
sinon.reset();
});

it('returns the correct response', async () => {
const configValues = getAccessManagementServiceAPIOverrides(pactSetUp.provider.mockService.baseUrl);
sandbox.stub(config, 'get').callsFake((prop) => {
return configValues[prop];
});

const { getAccessRolesByCaseId } = requireReloaded('../../../../roleAccess/index');

const req = mockReq({
headers: {
'Authorization': 'Bearer someAuthorizationToken',
'ServiceAuthorization': 'Bearer someServiceAuthorizationToken',
'content-type': 'application/vnd.uk.gov.hmcts.role-assignment-service.post-assignment-query-request+json;charset=UTF-8;version=2.0'
},
body: {
caseId: caseId
}

});
let returnedResponse = null;
const response = mockRes();
response.send = (ret) => {
returnedResponse = ret;
};

try {
await getAccessRolesByCaseId(req, response, next);
assertResponses(returnedResponse);
pactSetUp.provider.verify();
pactSetUp.provider.finalize();
} catch (err) {
pactSetUp.provider.verify();
pactSetUp.provider.finalize();
throw new Error(err);
}
});
});
});

function assertResponses(dto: any) {
expect(dto.length).to.be.equal(1);
expect(dto[0].actions[0].id).to.be.equal('reallocate');
expect(dto[0].actions[0].title).to.be.equal('Reallocate');
expect(dto[0].actorId).to.be.equal('271ebdd4-f757-492d-b57f-101b8e47c90e');
expect(dto[0].end).to.be.equal(null);
expect(dto[0].id).to.be.equal('b83acc2f-6720-4cf9-a1f8-52367c35963d');
expect(dto[0].roleId).to.be.equal(null);
expect(dto[0].location).to.be.equal(null);
expect(dto[0].roleCategory).to.be.equal('LEGAL_OPERATIONS');
expect(dto[0].roleName).to.be.equal('case-manager');
expect(dto[0].start).to.be.equal('2022-09-15T23:00:00Z');
expect(dto[0].created).to.be.equal('2022-09-16T13:06:44.295367Z');
expect(dto[0].notes).to.be.equal('No reason for case access given');
expect(dto[0].requestedRole).to.be.equal(null);
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
"@angular/platform-browser-dynamic": "^11.2.14",
"@angular/router": "^11.2.14",
"@edium/fsm": "^2.1.2",
"@hmcts/ccd-case-ui-toolkit": "6.19.15",
"@hmcts/ccd-case-ui-toolkit": "6.19.15-restricted-case-access",
"@hmcts/ccpay-web-component": "5.2.8",
"@hmcts/frontend": "0.0.50-alpha",
"@hmcts/media-viewer": "2.9.5",
Expand Down
1 change: 1 addition & 0 deletions src/app/app.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const featureNames = {
booking: 'mc-booking-active',
mcHearingsFeature: 'mc-hearings-jurisdictions',
excludedRolesForCaseTabs: 'mc-excluded-roles-case-tabs',
enableRestrictedCaseAccess: 'enable-restricted-case-access',
enableCaseFileViewVersion1_1: 'enable-case-file-view-version-1-1'
};

Expand Down
11 changes: 11 additions & 0 deletions src/app/services/ccd-config/ccd-case.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ export class AppConfig extends AbstractAppConfig {
}
});

this.featureToggleService.getValue(AppConstants.FEATURE_NAMES.enableRestrictedCaseAccess, false).subscribe({
next: (val) => this.config = {
...this.config,
enable_restricted_case_access: val
}
});

this.featureToggleService.getValue(AppConstants.FEATURE_NAMES.enableCaseFileViewVersion1_1, false).subscribe({
next: (val) => this.config = {
...this.config,
Expand Down Expand Up @@ -243,6 +250,10 @@ export class AppConfig extends AbstractAppConfig {
return this.config.case_data_store_api_url;
}

public getEnableRestrictedCaseAccessConfig(): boolean {
return this.config.enable_restricted_case_access;
}

public getEnableCaseFileViewVersion1_1(): boolean {
return this.config.enable_case_file_view_version_1_1;
}
Expand Down
20 changes: 14 additions & 6 deletions src/cases/case-feature.routes.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import { ModuleWithProviders } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CaseResolver, editorRouting, viewerRouting as caseViewRouting } from '@hmcts/ccd-case-ui-toolkit';
import { CaseResolver, viewerRouting as caseViewRouting, editorRouting } from '@hmcts/ccd-case-ui-toolkit';
import {
CaseCreateSubmitComponent,
CaseDetailsHomeComponent,
CaseFilterComponent,
CaseHearingsComponent,
CaseHomeComponent,
CaseListComponent,
CasesCreateComponent,
CaseShareCompleteComponent,
CaseShareComponent,
CaseShareConfirmComponent
CaseShareConfirmComponent,
CasesCreateComponent
} from './containers';
import { CaseLoaderComponent } from './containers/case-loader/case-loader.component';
import { CaseSearchComponent } from './containers/case-search/case-search.component';
import { CaseViewerContainerComponent } from './containers/case-viewer-container/case-viewer-container.component';
import { RolesAndAccessContainerComponent
} from './containers/roles-and-access-container/roles-and-access-container.component';
import { RestrictedCaseAccessContainerComponent } from './containers/restricted-case-access-container/restricted-case-access-container.component';
import { RolesAndAccessContainerComponent } from './containers/roles-and-access-container/roles-and-access-container.component';
import { TasksContainerComponent } from './containers/tasks-container/tasks-container.component';
import { RestrictedCaseAccessGuard } from './guards/restricted-case-access-guard';
import { ActivityResolver } from './resolvers/activity.resolver';
import { CreateCaseEventTriggerResolver } from './resolvers/create-case-event-trigger.resolver';

Expand Down Expand Up @@ -129,11 +130,18 @@ export const ROUTES: Routes = [
data: {
title: 'Case Details'
}
},
{
path: 'restricted-case-access/:cid',
component: RestrictedCaseAccessContainerComponent,
canActivate: [RestrictedCaseAccessGuard],
data: {
title: 'Restricted case access'
}
}
]
}

];

export const casesRouting: ModuleWithProviders<RouterModule> = RouterModule.forChild(ROUTES);

6 changes: 5 additions & 1 deletion src/cases/cases.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ import { CreateCaseEventTriggerResolver } from './resolvers/create-case-event-tr
// from services
import * as fromServices from './services';
import { effects, reducers } from './store';
import { RestrictedCaseAccessComponent } from './components/restricted-case-access/restricted-case-access.component';
import { RestrictedCaseAccessContainerComponent } from './containers/restricted-case-access-container/restricted-case-access-container.component';
import { RestrictedCaseAccessGuard } from './guards/restricted-case-access-guard';

@NgModule({
imports: [
Expand Down Expand Up @@ -134,7 +137,8 @@ import { effects, reducers } from './store';
IsCompoundPipe,
CcdCYAPageLabelFilterPipe,
CaseFileViewService,
JurisdictionService
JurisdictionService,
RestrictedCaseAccessGuard
]
})
/**
Expand Down
10 changes: 6 additions & 4 deletions src/cases/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AllocateARoleLinkComponent } from './allocate-a-role/allocate-a-role-li
import { AlertComponent } from './case-alert/alert.component';
import { CaseHearingsListComponent } from './case-hearings-list/case-hearings-list.component';
import { CaseTaskComponent } from './case-task/case-task.component';
import { RestrictedCaseAccessComponent } from './restricted-case-access/restricted-case-access.component';
import { RoleAccessSectionComponent } from './role-access-section/role-access-section.component';
import { RolesAndAccessComponent } from './roles-and-access/roles-and-access.component';
import { TaskAlertBannerComponent } from './task-alert-banner/task-alert-banner.component';
Expand All @@ -17,12 +18,13 @@ export const components: any[] = [
CaseTaskComponent,
AllocateARoleLinkComponent,
RoleAccessSectionComponent,
CaseHearingsListComponent
CaseHearingsListComponent,
RestrictedCaseAccessComponent
];

export * from './allocate-a-role/allocate-a-role-link.component';
export * from './case-alert/alert.component';
export * from './task-alert-banner/task-alert-banner.component';
export * from './case-hearings-list/case-hearings-list.component';
export * from './case-task/case-task.component';
export * from './allocate-a-role/allocate-a-role-link.component';
export * from './role-access-section/role-access-section.component';
export * from './case-hearings-list/case-hearings-list.component';
export * from './task-alert-banner/task-alert-banner.component';
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<h2 class="govuk-heading-m">{{ 'Users with access' | rpxTranslate }}</h2>
<table class="govuk-table">
<thead class="govuk-table__head">
<tr class="govuk-table__row">
<th class="govuk-table__header govuk-table-column-header" scope="col">{{ 'User' | rpxTranslate }}</th>
<th class="govuk-table__header govuk-table-column-header" scope="col">{{ 'Case role' | rpxTranslate }}</th>
<th class="govuk-table__header govuk-table-column-actions" scope="col">{{ 'Email address' | rpxTranslate }}</th>
</tr>
</thead>
<tbody class="govuk-table__body">
<tr class="govuk-table__row" *ngFor="let case of restrictedCases">
<td class="govuk-table__cell">{{case.user}}</td>
<td class="govuk-table__cell">{{case.role}}</td>
<td class="govuk-table__cell">{{case.email}}</td>
</tr>
</tbody>
</table>
Loading