Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package uk.gov.hmcts.reform.preapi.controllers;

import io.restassured.response.Response;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import uk.gov.hmcts.reform.preapi.controllers.params.TestingSupportRoles;
import uk.gov.hmcts.reform.preapi.util.FunctionalTestBase;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

public class TermsAndConditionsFT extends FunctionalTestBase {

@Test
@DisplayName("Should return the latest APP terms when the feature flag is enabled")
void getLatestAppTerms() {
// Create an APP terms and conditions record for testing
Response created = doPostRequest("/testing-support/create-terms-and-conditions/APP",
TestingSupportRoles.SUPER_USER);
assertResponseCode(created, 200);
String createdId = created.body().jsonPath().getString("termsId");

// Get latest APP terms and conditions
Response response = doGetRequest("/app-terms-and-conditions/latest", TestingSupportRoles.SUPER_USER);
assertResponseCode(response, 200);
assertThat(response.body().jsonPath().getString("id")).isEqualTo(createdId);
assertThat(response.body().jsonPath().getString("type")).isEqualTo("APP");
}

@Test
@DisplayName("Should return the latest Portal terms")
void getLatestPortalTerms() {
// Create a Portal terms and conditions record for testing
Response created = doPostRequest("/testing-support/create-terms-and-conditions/PORTAL",
TestingSupportRoles.SUPER_USER);
assertResponseCode(created, 200);
String createdId = created.body().jsonPath().getString("termsId");

// Get latest Portal terms and conditions
Response response = doGetRequest("/portal-terms-and-conditions/latest", TestingSupportRoles.SUPER_USER);
assertResponseCode(response, 200);
assertThat(response.body().jsonPath().getString("id")).isEqualTo(createdId);
assertThat(response.body().jsonPath().getString("type")).isEqualTo("PORTAL");
}
}
3 changes: 3 additions & 0 deletions src/functionalTest/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ mediakind.symmetricKey=${SYMMETRIC_KEY:}
media-service=${MEDIA_SERVICE:MediaKind}
editing.enable=true

feature-flags.dynatrace-terms-and-conditions.enabled=true
feature-flags.dynatrace-terms-and-conditions.cut-off-date=2026-07-15

#logging.level.uk.gov.hmcts.reform.preapi.actuator.PreApiHealthIndicator: DEBUG
#logging.level.uk.gov.hmcts.reform.preapi.email.StopLiveEventNotifierFlowClient: DEBUG
#logging.level.uk.gov.hmcts.reform.preapi.email.CaseStateChangeNotifierFlowClient: DEBUG
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package uk.gov.hmcts.reform.preapi.services;

import jakarta.transaction.Transactional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.TestPropertySource;
import uk.gov.hmcts.reform.preapi.dto.TermsAndConditionsDTO;
import uk.gov.hmcts.reform.preapi.entities.TermsAndConditions;
import uk.gov.hmcts.reform.preapi.enums.TermsAndConditionsType;
import uk.gov.hmcts.reform.preapi.util.HelperFactory;
import uk.gov.hmcts.reform.preapi.utils.IntegrationTestBase;

import java.time.LocalDate;

import static org.assertj.core.api.Assertions.assertThat;

@TestPropertySource(properties = {
"feature-flags.dynatrace-terms-and-conditions.enabled=false",
"feature-flags.dynatrace-terms-and-conditions.cut-off-date=2026-07-15"
})
public class TermsAndConditionsServiceCutoffUsedIT extends IntegrationTestBase {

@Autowired
private TermsAndConditionsService termsAndConditionsService;

@Test
@Transactional
public void getLatestAppTermsAndConditionsCreatedBeforeCutoff() {
//Create an app terms that is before cutoff date
TermsAndConditions termsCreatedBeforeCutoff =
HelperFactory.createTermsAndConditions(TermsAndConditionsType.APP, "<h1>Terms and Conditions</h1>");
entityManager.persist(termsCreatedBeforeCutoff);
termsCreatedBeforeCutoff
.setCreatedAt(java.sql.Timestamp.valueOf(LocalDate.of(2026, 7, 14).atStartOfDay()));
entityManager.flush();
entityManager.clear();

//Create an app terms that is after cutoff date
TermsAndConditions termsCreatedAfterCutoff =
HelperFactory.createTermsAndConditions(TermsAndConditionsType.APP, "<h1>Terms and Conditions</h1>");
entityManager.persist(termsCreatedAfterCutoff);
termsCreatedAfterCutoff
.setCreatedAt(java.sql.Timestamp.valueOf(LocalDate.of(2026, 7, 16).atStartOfDay()));
entityManager.flush();
entityManager.clear();

TermsAndConditionsDTO result =
termsAndConditionsService.getLatestTermsAndConditions(TermsAndConditionsType.APP);

assertThat(result.getId()).isEqualTo(termsCreatedBeforeCutoff.getId());
assertThat(result.getCreatedAt())
.isBefore(java.sql.Timestamp.valueOf(LocalDate.of(2026, 7, 15).atStartOfDay()));
}

@Test
@Transactional
public void getLatestPortalTermsAndConditionsIgnoresCutoff() {

//Create a portal terms that is before cutoff date
TermsAndConditions termsCreatedBeforeCutoff =
HelperFactory.createTermsAndConditions(TermsAndConditionsType.PORTAL, "# Terms and Conditions");
entityManager.persist(termsCreatedBeforeCutoff);
termsCreatedBeforeCutoff
.setCreatedAt(java.sql.Timestamp.valueOf(LocalDate.of(2026, 7, 14).atStartOfDay()));
entityManager.flush();
entityManager.clear();

//Create a portal terms that is after cutoff date
TermsAndConditions termsCreatedAfterCutoff =
HelperFactory.createTermsAndConditions(TermsAndConditionsType.PORTAL, "# Terms and Conditions");
entityManager.persist(termsCreatedAfterCutoff);
termsCreatedAfterCutoff
.setCreatedAt(java.sql.Timestamp.valueOf(LocalDate.of(2026, 7, 16).atStartOfDay()));
entityManager.flush();
entityManager.clear();

TermsAndConditionsDTO result
= termsAndConditionsService.getLatestTermsAndConditions(TermsAndConditionsType.PORTAL);

assertThat(result.getCreatedAt())
.isAfter(java.sql.Timestamp.valueOf(LocalDate.of(2026, 7, 15).atStartOfDay()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.TestPropertySource;
import uk.gov.hmcts.reform.preapi.entities.TermsAndConditions;
import uk.gov.hmcts.reform.preapi.enums.TermsAndConditionsType;
import uk.gov.hmcts.reform.preapi.util.HelperFactory;
import uk.gov.hmcts.reform.preapi.utils.IntegrationTestBase;

import static org.assertj.core.api.Assertions.assertThat;

@TestPropertySource(properties = {
"feature-flags.dynatrace-terms-and-conditions.enabled=true",
"feature-flags.dynatrace-terms-and-conditions.cut-off-date=2026-07-15"
})
class TermsAndConditionsServiceIT extends IntegrationTestBase {
@Autowired
private TermsAndConditionsService termsAndConditionsService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
import uk.gov.hmcts.reform.preapi.entities.TermsAndConditions;
import uk.gov.hmcts.reform.preapi.enums.TermsAndConditionsType;

import java.sql.Timestamp;
import java.util.Optional;
import java.util.UUID;

@Repository
public interface TermsAndConditionsRepository extends JpaRepository<TermsAndConditions, UUID> {
Optional<TermsAndConditions> findFirstByTypeOrderByCreatedAtDesc(TermsAndConditionsType type);

Optional<TermsAndConditions> findFirstByTypeAndCreatedAtBeforeOrderByCreatedAtDesc(
TermsAndConditionsType type, Timestamp timestamp);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,50 @@

import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import uk.gov.hmcts.reform.preapi.dto.TermsAndConditionsDTO;
import uk.gov.hmcts.reform.preapi.entities.TermsAndConditions;
import uk.gov.hmcts.reform.preapi.enums.TermsAndConditionsType;
import uk.gov.hmcts.reform.preapi.exception.NotFoundException;
import uk.gov.hmcts.reform.preapi.repositories.TermsAndConditionsRepository;

import java.sql.Timestamp;
import java.time.LocalDate;
import java.util.Optional;

@Service
public class TermsAndConditionsService {
private final TermsAndConditionsRepository termsAndConditionsRepository;

@Value("${feature-flags.dynatrace-terms-and-conditions.cut-off-date}")
private LocalDate cutOffDate;

@Value("${feature-flags.dynatrace-terms-and-conditions.enabled}")
private boolean isDynatraceAppTermsEnabled;


@Autowired
public TermsAndConditionsService(TermsAndConditionsRepository termsAndConditionsRepository) {
this.termsAndConditionsRepository = termsAndConditionsRepository;
}

/**
* Returns the latest terms for the given type.
* For `PORTAL`, always returns the newest terms.
* Temporarily - For `APP`, returns either the newest terms or the newest terms before the configured cutoff date,
* depending on `feature-flags.dynatrace-terms-and-conditions.enabled` property.
* @param type terms and conditions type (`APP` or `PORTAL`)
* @return latest applicable terms and conditions
*/
public TermsAndConditionsDTO getLatestTermsAndConditions(@NotNull TermsAndConditionsType type) {
return termsAndConditionsRepository.findFirstByTypeOrderByCreatedAtDesc(type)
.map(TermsAndConditionsDTO::new)
boolean useLatestTerms = type == TermsAndConditionsType.PORTAL || isDynatraceAppTermsEnabled;
Optional<TermsAndConditions> terms = useLatestTerms
? termsAndConditionsRepository.findFirstByTypeOrderByCreatedAtDesc(type)
: termsAndConditionsRepository.findFirstByTypeAndCreatedAtBeforeOrderByCreatedAtDesc(
type, Timestamp.valueOf(cutOffDate.atStartOfDay()));

return terms.map(TermsAndConditionsDTO::new)
.orElseThrow(() -> new NotFoundException("Terms and conditions of type: " + type));
}
}
3 changes: 3 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ feature-flags:
hide-reencoded-recordings: ${HIDE_REENCODED_RECORDINGS:true}
cleanup-null-duration:
upsert-enabled: ${ENABLE_NULL_DURATION_UPSERT:false}
dynatrace-terms-and-conditions:
enabled: ${ENABLE_TERMS_AND_CONDITIONS:false}
cut-off-date: ${TERMS_AND_CONDITIONS_CUTOFF_DATE:2026-07-15}

#logging:
# level:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
INSERT into public.terms_and_conditions VALUES
(
gen_random_uuid(),
E'<h1>Pre-Recorded Evidence (PRE) Service</h1>\n<h2>Terms & Conditions (including Acceptable Use)<br/>(All Users)</h2>\n<p>July 2026</p>\n\n<h2>Introduction</h2>\n<b>This PRE Service is provided by HM Courts and Tribunals Service (HMCTS) Unauthorised use is a criminal offence under the Computer Misuse Act 1990, and you should disconnect immediately if you have not been authorised to use this system. Unauthorised access is prevented by two factor authentication.</b>\n<p>The PRE Service provides access to pre-recorded cross examination recordings. It is supplied to individual users (The User) in accordance with the following Terms & Conditions.</p>\n<p>The User understands that use of the Service will be taken as their acceptance of these Terms & Conditions and that they are fully aware of their responsibilities in relation to the use of the service as set out in the Terms & Conditions on the page below.</p>\n\n\n<h2>Cookies</h2>\n<p>We use cookies to help us understand how the service is being used and to improve it. Cookies we use:</p>\n<table>\n <thead>\n <tr>\n <th>Name</th>\n <th>Purpose</th>\n <th>Expires</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>dtCookie dtPC dtSa rxVisitor rxtv</td>\n <td>Please see the <a href="https://docs.dynatrace.com/docs/manage/data-privacy-and-security/data-privacy/cookies#dynatrace-rum-cookies">Dynatrace website</a> for up-to-date information on the usage of each of these cookies.</td>\n <td>Please see the <a href="https://docs.dynatrace.com/docs/manage/data-privacy-and-security/data-privacy/cookies#dynatrace-rum-cookies">Dynatrace website</a> for up-to-date information on the expiry of these cookies.</td>\n </tr>\n </tbody>\n</table>\n<h2>Terms and Conditions</h2>\n<ol>\n <li>\n I have a legitimate need to use the PRE Service, and will only access recordings associated with media of cases where I have a business need to do so.\n </li>\n <li>\n I will comply with UK Data Protection Act 2018, relevant privacy regulations and all professional codes of conduct by which I am bound and will ensure all information accessed through the PRE Service is treated accordingly. I acknowledge that any breach of these provisions may result in my access to the PRE Service being suspended or terminated. Any breach may also result in disciplinary action.\n </li>\n <li>\n I will seek to prevent inadvertent disclosure of information by taking care when viewing the recording on the PRE Service. I will make every effort to ensure my screen is not visible to others who do not have a legitimate reason to see the recording.\n </li>\n <li>\n I agree to be accountable for any use of the PRE Service using my unique user credentials (user ID, password, log-in) and e-mail address. As such, I understand that:\n <ol type="a">\n <li>\n I must protect my PRE Service credentials for access to the service.\n </li>\n <li>\n I must not share my PRE Service credentials.\n </li>\n <li>\n I must report actual or suspected disclosure of this information to HMCTS through the local court.\n </li>\n <li>\n I will not use another person’s credentials to access the PRE Service.\n </li>\n </ol>\n </li>\n <li>\n I will ensure that computing devices connected to the PRE Service will not be left unattended unless they are physically secure and have a clear password locked screen.\n </li>\n <li>\n I will take precautions to protect all computer media and portable computers/devices that will be used to access the PRE Service at all times (e.g., by not leaving a device unattended or on display in a public space).\n </li>\n <li>\n I will not share any video recording or other content accessed via the PRE Service with any other party or persons, unless they have a legal right to view the recording.\n </li>\n <li>\n I will not access the PRE Service from public shared devices (e.g. those in internet cafes).\n </li>\n <li>\n I will only access the PRE Service from devices which have appropriate security controls installed and which are maintained to be up to date (including, as appropriate, firewalls, anti-virus & spyware software and operating system security patches).\n </li>\n <li>\n I will not attempt to bypass or subvert system security controls.\n </li>\n <li>\n When using Wi-Fi, I will only access the PRE Service using secure internet connection or using secure internet service. I will not ‘trust’ or ‘accept’ invalid security for web site certificates.\n </li>\n <li>\n I confirm that I will only connect to the PRE Service from within the United Kingdom and will not attempt to access the PRE Service from a location that is outside the United Kingdom, without prior authorisation.\n </li>\n <li>\n I understand that HMCTS and the Ministry of Justice (MOJ) reserves the right to audit my usage and investigate security incidents and confirm that, should such an investigation be necessary, I will provide any necessary support to the best of my ability.\n </li>\n <li>\n I agree to report any data losses, breaches or security incidents by contacting the DTS Service Desk and Line Manager immediately.\n </li>\n <li>\n In the event of a suspected breach of these Terms & Conditions HMCTS reserves the right to investigate and if a breach has occurred, to impose appropriate sanctions. This can range from a warning and instructions to improve practice, to temporary suspension or reduction in the service availability, to the potential complete withdrawal of service should the breach impact adversely users of the PRE Service, and other associated services.\n </li>\n <li>\n I will use the PRE Service in accordance with the appropriate user guides and agree to notify the helpdesk and line manager immediately if there is any change in my circumstances or role that affect my access to the Pre Service. This includes (but is not limited to) changes to my circumstances or role so that certain levels of access are no longer appropriate. I will inform the helpdesk and line manager prior to leaving my role in order that my account may be deleted.\n </li>\n</ol>\n\n<p>Declaration:</p>\n<p>I declare that I am fully aware of my responsibilities in relation to the use of the Service as set out in the above Terms & Conditions.</p>',
'APP'
);
Loading
Loading