Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
82a277f
HDPI-7052: Pin Jenkins library to Infrastructure@2.4.5
arun-hmcts Jul 15, 2026
c89ea7d
HDPI-7052: preview chart - ORM env, PRM scheduling, group access filt…
arun-hmcts Jul 15, 2026
c50276b
HDPI-7052: pin Jenkins library to existing tag 2.4.4
arun-hmcts Jul 17, 2026
9405562
HDPI-7052: align group access config to agreed RRFM values
arun-hmcts Jul 17, 2026
93fa3b7
HDPI-7052: LOCALAUTH profile first while SDK keeps one row per access…
arun-hmcts Jul 17, 2026
c650d94
HDPI-7052: switch sonar project key to pcs-api-2 (#2199)
scottstewart-sl Jul 17, 2026
89bd568
HDPI-7052: uppercase group template - def store requires service name…
arun-hmcts Jul 17, 2026
8901770
HDPI-7052: trim config comment
arun-hmcts Jul 17, 2026
53ba9be
HDPI-7052: enable ORM professional refresh API on preview
arun-hmcts Jul 17, 2026
4046c90
HDPI-7052: restore ACR login on Jenkins agents in create-xlsx.sh
arun-hmcts Jul 17, 2026
9e6581f
HDPI-7052: use [SOLICITOR] group role with SDK bracket-ACL fix (99.1-…
Jul 20, 2026
d25c91f
HDPI-7052: stamp cases with group access key (WK8GIHE interim) + seri…
Jul 21, 2026
dc53d1b
HDPI-7052: repoint def-processor image to live PR-348 tag (64dd53f pu…
Jul 21, 2026
baf2917
HDPI-7052: merge spike branch - master catch-up + V133-135 migrations
arun-hmcts Jul 23, 2026
f6d0c93
HDPI-7052: suppress ES rest-client 8.18.8 CVEs (transitive, cftlib-only)
arun-hmcts Jul 23, 2026
6cb97c7
HDPI-7052: stub accessType in CaseTypeTest so shutter tests pass
arun-hmcts Jul 23, 2026
82a3635
HDPI-7052: preview uses shared AAT RAS/ORM, drop per-PR AM services
arun-hmcts Jul 23, 2026
8b80ce8
HDPI-7052: self-contained preview - per-PR RAS/ORM, refresh script
arun-hmcts Jul 24, 2026
ce2a20e
HDPI-7052: trim verbose comments on group-access config
arun-hmcts Jul 24, 2026
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
2 changes: 1 addition & 1 deletion bin/create-xlsx.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ az acr login --name hmctsprod --subscription DCD-CNP-PROD
docker run --rm --name "json2xlsx" \
-v "$run_dir/build/definitions/${case_type}:/tmp/ccd-input" \
-v "$run_dir/build/definitions:/tmp/ccd-output" \
hmctsprod.azurecr.io/ccd/definition-processor:pr-348-64dd53f \
hmctsprod.azurecr.io/ccd/definition-processor:pr-348-7561002 \
json2xlsx -D /tmp/ccd-input -o /tmp/ccd-output/"${ccd_definition_file}"

done
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ plugins {
id 'org.flywaydb.flyway' version "$flywayVersion"
id 'org.sonarqube' version '7.3.1.8318'
id 'net.serenity-bdd.serenity-gradle-plugin' version "$serenityBddVersion"
id 'hmcts.ccd.sdk' version 'HDPI-6258-generate-accessType-accessTypeRole-json-74.1-20933aed'
id 'hmcts.ccd.sdk' version 'HDPI-6258-generate-accessType-accessTypeRole-json-99.1-ee757057'
id 'com.github.hmcts.rse-cft-lib' version '0.19.2220'
id 'io.freefair.lombok' version '9.5.0'
id 'au.com.dius.pact' version '4.7.3'
Expand Down
2 changes: 1 addition & 1 deletion charts/pcs-api/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ appVersion: "1.0"
description: A Helm chart for pcs-api App
name: pcs-api
home: https://github.com/hmcts/pcs-api
version: 0.0.76
version: 0.0.79
maintainers:
- name: HMCTS pcs team
dependencies:
Expand Down
5 changes: 5 additions & 0 deletions charts/pcs-api/values.ccd.preview.template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ ccd:
ELASTIC_SEARCH_HOSTS: "http://{{ .Release.Name }}-es-master:9200"
CCD_S2S_AUTHORISED_SERVICES_CASE_USER_ROLES: "aac_manage_case_assignment,pcs_api,pcs_frontend"
CASE_DOCUMENT_AM_URL: http://${SERVICE_NAME}-cdam
ENABLE_CASE_GROUP_ACCESS_FILTERING: true
ingressHost: ccd-data-store-api-${SERVICE_FQDN}
autoscaling:
enabled: false
Expand Down Expand Up @@ -342,6 +343,10 @@ am-org-role-mapping-service:
REFRESH_BULK_ASSIGNMENT_INCLUDE_BOOKINGS: true
DB_FEATURE_FLAG_ENABLE: st_cic_wa_1_0
ORM_ENV: pr
CCD_DEF_STORE_APP_URL: http://${SERVICE_NAME}-ccd-definition-store
PROFESSIONAL_REF_APP_URL: http://rd-professional-api-aat.service.core-compute-aat.internal
PROFESSIONAL_SCHEDULING_ENABLED: true
PROFESSIONAL_REFRESH_API_ENABLED: true

aac-manage-case-assignment:
enabled: true
Expand Down
6 changes: 6 additions & 0 deletions config/owasp/suppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,11 @@
<notes><![CDATA[angus-activation-2.0.3.jar]]></notes>
<cve>CVE-2025-7962</cve>
</suppress>
<suppress until="2026-08-15">
<notes><![CDATA[elasticsearch-rest-client-8.18.8.jar - transitive via rse-cft-lib (local/test infra); branch on UUID lineage, master already on ES 9.4.2]]></notes>
<cve>CVE-2025-37731</cve>
<cve>CVE-2025-68384</cve>
<cve>CVE-2025-68390</cve>
</suppress>
<!--End of false positives section -->
</suppressions>
40 changes: 40 additions & 0 deletions docs/group-access/refresh.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
# Trigger PRM refresh for a professional user on a PCS PR preview, then show RAS result.
# Requires HMCTS VPN (the S2S lease URL is internal). jq required.
#
# export IDAM_CLIENT_SECRET=<pcs-frontend client secret> # from the Postman env or pcs-aat vault
# ./refresh.sh <userId> [pr-number]
# ./refresh.sh 3b18bab9-33a3-420f-b37e-b63e73862828 2179 # johnwoo on pr-2179
set -euo pipefail

USER_ID="${1:?usage: refresh.sh <userId> [pr-number]}"
PR="${2:-2179}"
CLIENT_SECRET="${IDAM_CLIENT_SECRET:?export IDAM_CLIENT_SECRET (pcs-frontend) first}"
IDAM_USER="${IDAM_USER:-johnwoo@johnwoo.com}"
IDAM_PASS="${IDAM_PASS:-Testing12345}"

IDAM=https://idam-api.aat.platform.hmcts.net
S2S=http://rpe-service-auth-provider-aat.service.core-compute-aat.internal
ORM=https://am-org-role-mapping-service-pcs-api-pr-${PR}.preview.platform.hmcts.net
RAS=https://am-role-assignment-service-pcs-api-pr-${PR}.preview.platform.hmcts.net

echo "== auth =="
TOKEN=$(curl -s -X POST "$IDAM/o/token" \
-d grant_type=password -d "username=$IDAM_USER" -d "password=$IDAM_PASS" \
-d client_id=pcs-frontend -d "client_secret=$CLIENT_SECRET" -d 'scope=openid profile roles' \
| jq -r .access_token)
[ -n "$TOKEN" ] && [ "$TOKEN" != null ] || { echo "IDAM login failed"; exit 1; }
S2S_ORM=$(curl -s -X POST "$S2S/testing-support/lease" -H 'Content-Type: application/json' -d '{"microservice":"am_org_role_mapping_service"}')
S2S_CCD=$(curl -s -X POST "$S2S/testing-support/lease" -H 'Content-Type: application/json' -d '{"microservice":"ccd_data"}')
echo " ok (idam ${#TOKEN}, s2sOrm ${#S2S_ORM}, s2sCcd ${#S2S_CCD})"

echo "== PRM refresh $USER_ID =="
curl -s -o /dev/null -w ' -> HTTP %{http_code}\n' -X POST \
"$ORM/am/role-mapping/professional/refresh?userId=$USER_ID" \
-H "Authorization: Bearer $TOKEN" -H "ServiceAuthorization: Bearer $S2S_ORM"

sleep 5
echo "== RAS assignments for $USER_ID =="
curl -s "$RAS/am/role-assignments/actors/$USER_ID" \
-H "Authorization: Bearer $TOKEN" -H "ServiceAuthorization: Bearer $S2S_CCD" | jq .
# 422 on refresh + empty RAS = catalogue blocker (AM). Row present = AM added the role -> works.
55 changes: 39 additions & 16 deletions src/main/java/uk/gov/hmcts/reform/pcs/ccd/CaseType.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@

import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;

import static java.lang.System.getenv;
import static java.util.Optional.ofNullable;
import static uk.gov.hmcts.ccd.sdk.api.Permission.CRU;
import static uk.gov.hmcts.reform.pcs.ccd.ShowConditions.NEVER_SHOW;
import static uk.gov.hmcts.reform.pcs.ccd.domain.State.AWAITING_SUBMISSION_TO_HMCTS;

Expand Down Expand Up @@ -124,30 +126,51 @@ public void configure(final ConfigBuilder<PCSCase, State, AccessProfile> builder
.accessMandatory(true)
.accessDefault(true)
.display(false)
.description("Access to create cases")
.hintText("Access to create cases")
.displayOrder(1)
.liveTo("01/01/2027");
builder.accessType("prof-org-access")
.organisationProfileId("LOCALAUTH_PROFILE")
.accessMandatory(false)
.accessDefault(false)
.display(true)
.description("Can manage all cases associated with this organisation")
.hintText("Assign to Users to enable access to all cases associated with this organisation")
.displayOrder(2)
.liveTo("01/01/2027");

builder.accessTypeRole("create-cases")
.organisationProfileId("LOCALAUTH_PROFILE")
.organisationalRoleName(UserRole.SOLICITOR.getRole())
.liveTo("01/01/2027");
builder.accessTypeRole("prof-org-access")
.organisationProfileId("LOCALAUTH_PROFILE")
.groupRoleName(UserRole.SOLICITOR.getRole())
.caseAssignedRoleField(UserRole.PROFESSIONA_USER.getRole())
.groupAccessEnabled(true)
.caseAccessGroupIdTemplate("PCS:PCS:prof-org-access:solicitor:$ORGID$")
.liveTo("01/01/2027");

// SDK keeps only the first row per access type, so LOCALAUTH must lead.
List<String> groupAccessOrgProfiles = List.of(
"LOCALAUTH_PROFILE",
"SOLICITOR_PROFILE",
"OTHER_REALT_PROFILE",
"OTHER_PROP_PROFILE",
"OTHER_NFP_PROFILE",
"OTHER_CHARITY_PROFILE"
);

for (String orgProfile : groupAccessOrgProfiles) {
builder.accessType("prof-org-access")
.organisationProfileId(orgProfile)
.accessMandatory(false)
.accessDefault(false)
.display(true)
.description("Can manage all cases associated with this organisation")
.hintText("Assign to Users to enable access to all cases associated with this organisation")
.displayOrder(2)
.liveTo("01/01/2027");

builder.accessTypeRole("prof-org-access")
.organisationProfileId(orgProfile)
.groupRoleName(UserRole.SOLICITOR.getRole())
.caseAssignedRoleField(UserRole.PROFESSIONAL_USER.getRole())
.groupAccessEnabled(true)
// Uppercase service prefix required; def store rejects lowercase.
.caseAccessGroupIdTemplate("PCS:PCS:prof-org-access:solicitor:$ORGID$")
.liveTo("01/01/2027");
}

// State ACLs, else the data store filters matched cases out.
for (State state : State.values()) {
builder.grant(state, CRU, AccessProfile.SOLICITOR, AccessProfile.PROFESSIONAL_USER);
}


buildCaseListView(builder);
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/uk/gov/hmcts/reform/pcs/ccd/PCSCaseView.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ private SubmittedCase getSubmittedCase(long caseReference) {
.build();

setDerivedProperties(pcsCase, pcsCaseEntity);
setGroupAccessFields(pcsCase);

partiesView.setCaseFields(pcsCase, pcsCaseEntity);
claimView.setCaseFields(pcsCase, pcsCaseEntity);
Expand Down Expand Up @@ -199,6 +200,26 @@ private LocalDate getClaimIssueDateLocal(PcsCaseEntity pcsCaseEntity) {
.orElse(null);
}

// INTERIM: hardcoded WK8GIHE stamp for preview. TODO derive from the creator's org.
private void setGroupAccessFields(PCSCase pcsCase) {
uk.gov.hmcts.ccd.sdk.type.CaseAccessGroup caseAccessGroup =
uk.gov.hmcts.ccd.sdk.type.CaseAccessGroup.builder()
.caseAccessGroupId("PCS:PCS:prof-org-access:solicitor:WK8GIHE")
.caseAccessGroupType("CCD:all-cases-access")
.build();

ListValue<uk.gov.hmcts.ccd.sdk.type.CaseAccessGroup> wrapped =
ListValue.<uk.gov.hmcts.ccd.sdk.type.CaseAccessGroup>builder()
.id(UUID.nameUUIDFromBytes(caseAccessGroup.getCaseAccessGroupId().getBytes()).toString())
.value(caseAccessGroup)
.build();

uk.gov.hmcts.reform.pcs.ccd.domain.GroupAccessFields<uk.gov.hmcts.reform.pcs.ccd.accesscontrol.AccessProfile>
groupAccessFields = new uk.gov.hmcts.reform.pcs.ccd.domain.GroupAccessFields<>();
groupAccessFields.setCaseAccessGroups(List.of(wrapped));
pcsCase.setGroupAccessFields(groupAccessFields);
}

private void setDerivedProperties(PCSCase pcsCase, PcsCaseEntity pcsCaseEntity) {
boolean pcqIdSet = findPartyForCurrentUser(pcsCaseEntity)
.map(party -> party.getPcqId() != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public enum AccessProfile implements HasRole {
DEFENDANT_SOLICITOR("[DEFENDANTSOLICITOR]", CRU),
PCS_CASE_WORKER("caseworker-pcs", Set.of(R)),
PCS_SOLICITOR("caseworker-pcs-solicitor", CRU),
SOLICITOR("[SOLICITOR]", CRU),
PROFESSIONAL_USER("professional-user", CRU),

JUDGE("judge", CRU),
FEE_PAID_JUDGE("fee-paid-judge", CRU),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public enum UserRole implements HasRole {

CITIZEN("citizen", CRU, IDAM),
SOLICITOR("[SOLICITOR]", CRU, RAS),
PROFESSIONA_USER("professional-user", CRU, RAS),
PROFESSIONAL_USER("professional-user", CRU, RAS),
DEFENDANT("[DEFENDANT]", CRU, RAS),
CLAIMANT_SOLICITOR("[CLAIMANTSOLICITOR]", CRU, RAS),
DEFENDANT_SOLICITOR("[DEFENDANTSOLICITOR]", CRU, RAS),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@

import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
import lombok.Data;
import uk.gov.hmcts.ccd.sdk.api.CCD;
import uk.gov.hmcts.ccd.sdk.api.HasRole;
import uk.gov.hmcts.ccd.sdk.type.CaseAccessGroup;
import uk.gov.hmcts.ccd.sdk.type.ChangeOrganisationRequest;
import uk.gov.hmcts.ccd.sdk.type.ListValue;
import uk.gov.hmcts.ccd.sdk.type.Organisation;
import uk.gov.hmcts.ccd.sdk.type.OrganisationPolicy;
import uk.gov.hmcts.reform.pcs.ccd.accesscontrol.AccessProfile;

@Data
public class GroupAccessFields<R extends HasRole> {

@JsonProperty("CaseAccessGroups")
@CCD
private List<CaseAccessGroup> caseAccessGroups;
private List<ListValue<CaseAccessGroup>> caseAccessGroups;

@JsonProperty("OrganisationField")
@CCD
Expand Down
4 changes: 4 additions & 0 deletions src/test/java/uk/gov/hmcts/reform/pcs/ccd/CaseTypeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ private void stubBuilderForConfigure() {
when(builder.searchCasesFields()).thenReturn(searchCasesBuilder);
when(builder.searchResultFields()).thenReturn(searchBuilder);
when(builder.workBasketResultFields()).thenReturn(searchBuilder);
when(builder.accessType(anyString()))
.thenReturn(AccessType.AccessTypeBuilder.builder("accessTypeId"));
when(builder.accessTypeRole(anyString()))
.thenReturn(AccessTypeRole.AccessTypeRoleBuilder.builder("accessTypeId"));
when(builder.tab("nextSteps", "Next steps")).thenReturn(TabBuilder.builder(PCSCase.class, utils));
when(builder.tab("summary", "Summary")).thenReturn(TabBuilder.builder(PCSCase.class, utils));
when(builder.tab("CaseHistory", "History")).thenReturn(TabBuilder.builder(PCSCase.class, utils));
Expand Down