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
Expand Up @@ -116,6 +116,7 @@ void getUserState_returnsNoUnitsForUnentitledUser(Boolean newLogin) throws Excep
.andExpect(header().string("ETag", "\"0\""))
.andExpect(jsonPath("$['user_id']").value(500000001))
.andExpect(jsonPath("$['username']").value("opal-test-2@HMCTS.NET"))
.andExpect(jsonPath("$['status']").value("active"))
.andExpect(jsonPath("$['business_unit_users']", hasSize(0)));

boolean testNewLogin = Boolean.TRUE.equals(newLogin);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ void addUser() throws Exception {
.andExpect(jsonPath("$['username']").value("j.s@example.com"))
.andExpect(jsonPath("$['subject']").value("kWiw5ddDf32"))
.andExpect(jsonPath("$['name']").value("john.smith"))
.andExpect(jsonPath("$['status']").value("CREATED"))
.andExpect(jsonPath("$['status']").value("active"))
.andExpect(jsonPath("$['version']").value(0));

jsonSchemaValidationService.validateOrError(body, GET_USER_STATE_RESPONSE_JSON);
Expand All @@ -116,7 +116,6 @@ void addUser() throws Exception {
assertEquals("j.s@example.com", rowData.get("token_preferred_username"));
assertEquals("kWiw5ddDf32", rowData.get("token_subject"));
assertEquals("john.smith", rowData.get("token_name"));
assertEquals("CREATED", rowData.get("status"));
assertEquals(0L, rowData.get("version_number"));
}

Expand All @@ -143,7 +142,6 @@ void updateUser_ok_1() throws Exception {
assertEquals("update-user@HMCTS.NET", rowData.get("token_preferred_username"));
assertEquals("BmMfmuTT9pEdG", rowData.get("token_subject"));
assertNull(rowData.get("token_name"));
assertEquals("CREATED", rowData.get("status"));
assertEquals(0L, rowData.get("version_number"));

// Act
Expand All @@ -162,7 +160,7 @@ void updateUser_ok_1() throws Exception {
.andExpect(jsonPath("$['username']").value("j.s@example.com"))
.andExpect(jsonPath("$['name']").value("john.smith"))
.andExpect(jsonPath("$['subject']").value("2cdF2g3Ds"))
.andExpect(jsonPath("$['status']").value("CREATED"))
.andExpect(jsonPath("$['status']").value("active"))
.andExpect(jsonPath("$['version']").value(1));

jsonSchemaValidationService.validateOrError(body, GET_USER_STATE_RESPONSE_JSON);
Expand All @@ -174,7 +172,6 @@ void updateUser_ok_1() throws Exception {
assertEquals("j.s@example.com", rowData.get("token_preferred_username"));
assertEquals("2cdF2g3Ds", rowData.get("token_subject"));
assertEquals("john.smith", rowData.get("token_name"));
assertEquals("CREATED", rowData.get("status"));
assertEquals(1L, rowData.get("version_number"));
}

Expand All @@ -188,7 +185,6 @@ void updateUser_ok_2() throws Exception {
assertEquals("update-user@HMCTS.NET", rowData.get("token_preferred_username"));
assertEquals("QeJjwoWnY-kBmMfm", rowData.get("token_subject"));
assertEquals("Pablo", rowData.get("token_name"));
assertEquals("active", rowData.get("status"));
assertEquals(7L, rowData.get("version_number"));

// Act
Expand Down Expand Up @@ -218,7 +214,6 @@ void updateUser_ok_2() throws Exception {
assertEquals("j.s@example.com", rowData.get("token_preferred_username"));
assertEquals("QeJjwoWnY-kBmMfm", rowData.get("token_subject"));
assertEquals("john.smith", rowData.get("token_name"));
assertEquals("active", rowData.get("status"));
assertEquals(8L, rowData.get("version_number"));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ void buildUserState_handlesUsersWithoutBusinessUnits() {
assertEquals(500000003L, result.getUserId());
assertEquals("test-user@HMCTS.NET", result.getUsername());
assertEquals("Pablo", result.getName());
assertEquals("active", result.getStatus().toString());
assertEquals("active", result.getStatus());
assertEquals(2L, result.getVersion().longValue());
assertTrue(result.getBusinessUnitUsers().isEmpty());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
-- Insert users from Flyway script V20240729_003
INSERT INTO users (user_id, token_preferred_username, password, description)
VALUES (500000000, 'opal-test@HMCTS.NET', 'password', 'User with 7 business units');
INSERT INTO users (user_id, token_preferred_username, password, description, created_date)
VALUES (500000000, 'opal-test@HMCTS.NET', 'password', 'User with 7 business units', CURRENT_TIMESTAMP);

INSERT INTO users (user_id, token_preferred_username, token_subject, status, description, token_name, version_number)
VALUES (500000001, 'opal-test-2@HMCTS.NET', 'GfsHbIMt49WjQ', NULL, 'User with no business units', NULL, 0),
(500000002, 'update-user@HMCTS.NET', 'BmMfmuTT9pEdG', 'CREATED', 'User for testing \`update\`', NULL, 0),
(500000003, 'test-user@HMCTS.NET', 'jjqwGAERGW43', 'active', 'Test User for testing', 'Pablo', 2),
(500000004, 'test-user@HMCTS.NET', '7324-fh42dEsr', 'active', 'Test User for testing', 'Pablo', 2),
(500000005, 'update-user@HMCTS.NET', 'QeJjwoWnY-kBmMfm', 'active', 'Test User for testing \`update\`', 'Pablo', 7),
(500000006, 'no-go-user@HMCTS.NET', '8hqucbw874fg3', 'active',
'User with business units but no entitlements', 'No Permissions', 3);
INSERT INTO users (user_id, token_preferred_username, token_subject, description, token_name, version_number,
created_date)
VALUES (500000001, 'opal-test-2@HMCTS.NET', 'GfsHbIMt49WjQ', 'User with no business units', NULL, 0,
CURRENT_TIMESTAMP),
(500000002, 'update-user@HMCTS.NET', 'BmMfmuTT9pEdG', 'User for testing \`update\`', NULL, 0,
CURRENT_TIMESTAMP),
(500000003, 'test-user@HMCTS.NET', 'jjqwGAERGW43', 'Test User for testing', 'Pablo', 2,
CURRENT_TIMESTAMP),
(500000004, 'test-user@HMCTS.NET', '7324-fh42dEsr', 'Test User for testing', 'Pablo', 2,
CURRENT_TIMESTAMP),
(500000005, 'update-user@HMCTS.NET', 'QeJjwoWnY-kBmMfm', 'Test User for testing \`update\`', 'Pablo', 7,
CURRENT_TIMESTAMP),
(500000006, 'no-go-user@HMCTS.NET', '8hqucbw874fg3', 'User with business units but no entitlements', 'No Permissions', 3,
CURRENT_TIMESTAMP);

-- Insert business units that are referenced in the business_unit_users script
INSERT INTO business_units (business_unit_id, business_unit_name, business_unit_code, business_unit_type,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package uk.gov.hmcts.reform.opal.config;

import java.time.Clock;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TimeConfiguration {

@Bean
public Clock systemClock() {
return Clock.systemDefaultZone();
}
}
11 changes: 3 additions & 8 deletions src/main/java/uk/gov/hmcts/reform/opal/entity/UserEntity.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package uk.gov.hmcts.reform.opal.entity;

import java.math.BigInteger;
import java.time.LocalDateTime;
import java.util.Optional;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
Expand Down Expand Up @@ -57,12 +56,8 @@ public class UserEntity implements Versioned {
@EqualsAndHashCode.Exclude
private String description;

@Column(name = "opal_domain_id")
private Short opalDomainId;

@Column(name = "status", length = 25)
@Enumerated(EnumType.STRING)
private UserStatus status;
@Column(name = "created_date", nullable = false)
private LocalDateTime createdDate;

@Column(name = "token_subject", length = 100, unique = true)
private String tokenSubject;
Expand Down
6 changes: 0 additions & 6 deletions src/main/java/uk/gov/hmcts/reform/opal/entity/UserStatus.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public interface UserMapper {

@Mapping(source = "tokenSubject", target = "subject")
@Mapping(source = "tokenName", target = "name")
@Mapping(target = "status", constant = "active")
UserDto toUserDto(UserEntity userEntity);

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public interface UserStateMapper {
@Mapping(source = "userEntity.username", target = "username")
@Mapping(source = "businessUnitUsers", target = "businessUnitUsers")
@Mapping(source = "userEntity.tokenName", target = "name")
@Mapping(source = "userEntity.status", target = "status")
@Mapping(target = "status", constant = "active")
@Mapping(source = "userEntity.version", target = "version")
UserStateDto toUserStateDto(UserEntity userEntity, List<BusinessUnitUserDto> businessUnitUsers);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import static uk.gov.hmcts.opal.common.logging.LogUtil.getRequestTimestamp;
import static uk.gov.hmcts.reform.opal.util.VersionUtils.verifyIfMatch;

import java.time.Clock;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -31,7 +33,6 @@
import uk.gov.hmcts.reform.opal.entity.BusinessUnitUserEntity;
import uk.gov.hmcts.reform.opal.entity.UserEntitlementEntity;
import uk.gov.hmcts.reform.opal.entity.UserEntity;
import uk.gov.hmcts.reform.opal.entity.UserStatus;
import uk.gov.hmcts.reform.opal.exception.ResourceConflictException;
import uk.gov.hmcts.reform.opal.mappers.UserMapper;
import uk.gov.hmcts.reform.opal.mappers.UserStateMapper;
Expand All @@ -58,6 +59,7 @@ public class UserPermissionsService implements UserPermissionsProxy {
private final UserMapper userMapper;
private final AccessTokenService tokenService;
private final SecurityEventLoggingService securityEventLoggingService;
private final Clock clock;


@Transactional(readOnly = true)
Expand Down Expand Up @@ -180,7 +182,7 @@ public UserDto addUser(String authHeaderValue) {
UserEntity userEntity = userRepository
.saveAndFlush(UserEntity.builder()
.username(claimSet.getClaim(PREFERRED_USERNAME_CLAIM).toString())
.status(UserStatus.CREATED)
.createdDate(LocalDateTime.now(clock))
.tokenSubject(claimSet.getSubject())
.tokenName(claimSet.getClaim(NAME_CLAIM).toString())
.versionNumber(0L)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
*
* OPAL Program
*
* MODULE : add_users_role_lifecycle_columns.sql
*
* DESCRIPTION : Amend USERS table for role-based permission lifecycle for User Management Tactical
*
* VERSION HISTORY:
*
* Date Author Version Nature of Change
* ---------- ------- -------- ------------------------------------------------------------------------------------------------
* 09/03/2026 C Cho 1.0 PO-2825 Amend USERS table for role-based permission lifecycle for User Management Tactical
*
**/

ALTER TABLE users
ADD COLUMN created_date timestamp DEFAULT CURRENT_TIMESTAMP,
ADD COLUMN activation_date timestamp,
ADD COLUMN suspension_start_date timestamp,
ADD COLUMN suspension_end_date timestamp,
ADD COLUMN suspension_reason varchar(250),
ADD COLUMN deactivation_date timestamp,
ADD COLUMN last_login_date timestamp;

COMMENT ON COLUMN users.created_date IS 'Date the user account was created in Opal.';
COMMENT ON COLUMN users.activation_date IS 'Date the user account was first activated.';
COMMENT ON COLUMN users.suspension_start_date IS 'Most recent start date for the user account being suspended.';
COMMENT ON COLUMN users.suspension_end_date IS 'Most recent date on which the user account suspension ends.';
COMMENT ON COLUMN users.suspension_reason IS 'Reason for suspension of the user account.';
COMMENT ON COLUMN users.deactivation_date IS 'Date the user account was disabled.';
COMMENT ON COLUMN users.last_login_date IS 'Date the user last logged into Opal.';

ALTER TABLE users
ALTER COLUMN created_date SET NOT NULL;

ALTER TABLE users
ALTER COLUMN created_date DROP DEFAULT;

ALTER TABLE users
ADD CONSTRAINT users_suspension_reason_cc
CHECK (suspension_start_date IS NULL OR suspension_reason IS NOT NULL);

ALTER TABLE users
DROP CONSTRAINT IF EXISTS user_domain_fk;

ALTER TABLE users
DROP COLUMN opal_domain_id,
DROP COLUMN status;
2 changes: 1 addition & 1 deletion src/main/resources/openapi/userState.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,4 @@ components:
minItems: 0
type: array
items:
$ref: './common.yaml#/components/schemas/BusinessUnitUser'
$ref: './common.yaml#/components/schemas/BusinessUnitUser'
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,16 @@
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.math.BigInteger;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
Expand Down Expand Up @@ -89,6 +90,9 @@ class UserPermissionsServiceTest {
@Mock
private SecurityEventLoggingService securityEventLoggingService;

@Spy
private Clock clock = Clock.fixed(Instant.parse("2026-04-02T12:30:00Z"), ZoneOffset.UTC);

@Spy
@InjectMocks
private UserPermissionsService service;
Expand Down Expand Up @@ -234,8 +238,8 @@ void testAddUser() throws Exception {
assertEquals(42L, response.getUserId());
assertEquals("opal-user@hmcts.net", response.getUsername());
assertEquals("hcv732JFVWhf3Fd", response.getSubject());
assertNull(response.getStatus());
assertEquals("John Smith", response.getName());
assertEquals("active", response.getStatus());
assertEquals(BigInteger.valueOf(4L), response.getVersion());
}

Expand All @@ -258,8 +262,8 @@ void testUpdateUser() throws Exception {
assertEquals(42L, response.getUserId());
assertEquals("j.s@example.com", response.getUsername());
assertEquals("hcv732JFVWhf3Fd", response.getSubject());
assertNull(response.getStatus());
assertEquals("john.smith", response.getName());
assertEquals("active", response.getStatus());
assertEquals(BigInteger.valueOf(4L), response.getVersion());
}

Expand All @@ -282,8 +286,8 @@ void testUpdateUser_2() throws Exception {
assertEquals(42L, response.getUserId());
assertEquals("j.s@example.com", response.getUsername());
assertEquals("hcv732JFVWhf3Fd", response.getSubject());
assertNull(response.getStatus());
assertEquals("john.smith", response.getName());
assertEquals("active", response.getStatus());
assertEquals(BigInteger.valueOf(4L), response.getVersion());
}

Expand Down
Loading