diff --git a/libs/limited-access/build.gradle.kts b/libs/limited-access/build.gradle.kts new file mode 100644 index 0000000000..2772992675 --- /dev/null +++ b/libs/limited-access/build.gradle.kts @@ -0,0 +1,20 @@ +import uk.gov.justice.digital.hmpps.extensions.ClassPathExtension + +dependencies { + compileOnly("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + + testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("org.springframework.boot:spring-boot-starter-data-jpa") + testImplementation(libs.bundles.mockito) +} + +configure { + jacocoExclusions = listOf( + "**/exception/**", + "**/config/**", + "**/entity**", + "**/logging/**" + ) +} diff --git a/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/limitedaccess/entity/LimitedAccess.kt b/libs/limited-access/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/LimitedAccess.kt similarity index 64% rename from projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/limitedaccess/entity/LimitedAccess.kt rename to libs/limited-access/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/LimitedAccess.kt index f0193f3442..af422a74ae 100644 --- a/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/limitedaccess/entity/LimitedAccess.kt +++ b/libs/limited-access/src/main/kotlin/uk/gov/justice/digital/hmpps/entity/LimitedAccess.kt @@ -1,27 +1,27 @@ -package uk.gov.justice.digital.hmpps.integrations.delius.limitedaccess.entity +package uk.gov.justice.digital.hmpps.entity import jakarta.persistence.Column import jakarta.persistence.Entity import jakarta.persistence.Id import jakarta.persistence.JoinColumn import jakarta.persistence.ManyToOne +import jakarta.persistence.Table import org.hibernate.annotations.Immutable import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query -import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.Person -import uk.gov.justice.digital.hmpps.user.AuditUser import java.time.LocalDateTime @Immutable @Entity class Exclusion( + @ManyToOne @JoinColumn(name = "offender_id") - val person: Person, + val person: LimitedAccessPerson, @ManyToOne @JoinColumn(name = "user_id") - val user: AuditUser, + val user: LimitedAccessUser, @Column(name = "exclusion_end_time") val end: LocalDateTime?, @@ -34,13 +34,14 @@ class Exclusion( @Immutable @Entity class Restriction( + @ManyToOne @JoinColumn(name = "offender_id") - val person: Person, + val person: LimitedAccessPerson, @ManyToOne @JoinColumn(name = "user_id") - val user: AuditUser, + val user: LimitedAccessUser, @Column(name = "restriction_end_time") val end: LocalDateTime?, @@ -50,16 +51,45 @@ class Restriction( val id: Long ) -interface UserAccessRepository : JpaRepository { +@Immutable +@Entity +@Table(name = "offender") +class LimitedAccessPerson( + + @Column(columnDefinition = "char(7)") + val crn: String, + + val exclusionMessage: String?, + val restrictionMessage: String?, + + @Id + @Column(name = "offender_id") + val id: Long +) + +@Immutable +@Entity +@Table(name = "user_") +class LimitedAccessUser( + + @Column(name = "distinguished_name") + val username: String, + + @Id + @Column(name = "user_id") + val id: Long +) + +interface UserAccessRepository : JpaRepository { @Query( """ select p.crn as crn, '' as exclusionMessage, p.restrictionMessage as restrictionMessage - from Person p where p.crn in :crns + from LimitedAccessPerson p where p.crn in :crns and exists (select r from Restriction r where r.person.id = p.id and (r.end is null or r.end > current_date )) and not exists (select r from Restriction r where upper(r.user.username) = upper(:username) and r.person.id = p.id and (r.end is null or r.end > current_date )) union select p.crn as crn, p.exclusionMessage as exclusionMessage, '' as restrictionMessage - from Person p where p.crn in :crns + from LimitedAccessPerson p where p.crn in :crns and exists (select e from Exclusion e where upper(e.user.username) = upper(:username) and e.person.id = p.id and (e.end is null or e.end > current_date )) """ ) @@ -71,3 +101,6 @@ interface PersonAccess { val exclusionMessage: String? val restrictionMessage: String? } + +fun PersonAccess.isExcluded() = !exclusionMessage.isNullOrBlank() +fun PersonAccess.isRestricted() = !restrictionMessage.isNullOrBlank() diff --git a/libs/limited-access/src/main/kotlin/uk/gov/justice/digital/hmpps/service/UserAccessService.kt b/libs/limited-access/src/main/kotlin/uk/gov/justice/digital/hmpps/service/UserAccessService.kt new file mode 100644 index 0000000000..5109240cab --- /dev/null +++ b/libs/limited-access/src/main/kotlin/uk/gov/justice/digital/hmpps/service/UserAccessService.kt @@ -0,0 +1,39 @@ +package uk.gov.justice.digital.hmpps.service + +import org.springframework.stereotype.Service +import uk.gov.justice.digital.hmpps.entity.PersonAccess +import uk.gov.justice.digital.hmpps.entity.UserAccessRepository +import uk.gov.justice.digital.hmpps.entity.isExcluded +import uk.gov.justice.digital.hmpps.entity.isRestricted + +@Service +class UserAccessService(private val uar: UserAccessRepository) { + fun userAccessFor(username: String, crns: List): UserAccess { + val limitations: Map> = uar.getAccessFor(username, crns).groupBy { it.crn } + return UserAccess(crns.map { limitations[it].combined(it) }) + } + + private fun List?.combined(crn: String): CaseAccess { + return if (this == null) { + CaseAccess(crn, userExcluded = false, userRestricted = false) + } else { + CaseAccess( + crn, + any { it.isExcluded() }, + any { it.isRestricted() }, + firstOrNull { it.isExcluded() }?.exclusionMessage, + firstOrNull { it.isRestricted() }?.restrictionMessage + ) + } + } +} + +data class CaseAccess( + val crn: String, + val userExcluded: Boolean, + val userRestricted: Boolean, + val exclusionMessage: String? = null, + val restrictionMessage: String? = null +) + +data class UserAccess(val access: List) diff --git a/libs/limited-access/src/test/kotlin/uk/gov/justice/digital/hmpps/service/UserAccessServiceTest.kt b/libs/limited-access/src/test/kotlin/uk/gov/justice/digital/hmpps/service/UserAccessServiceTest.kt new file mode 100644 index 0000000000..8b3a42aee9 --- /dev/null +++ b/libs/limited-access/src/test/kotlin/uk/gov/justice/digital/hmpps/service/UserAccessServiceTest.kt @@ -0,0 +1,79 @@ +package uk.gov.justice.digital.hmpps.service + +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.whenever +import uk.gov.justice.digital.hmpps.entity.PersonAccess +import uk.gov.justice.digital.hmpps.entity.UserAccessRepository + +@ExtendWith(MockitoExtension::class) +internal class UserAccessServiceTest { + @Mock + internal lateinit var uar: UserAccessRepository + + @InjectMocks + internal lateinit var userAccessService: UserAccessService + + @Test + fun `user limited access is correctly returned`() { + givenLimitedAccessResults() + val res = userAccessService.userAccessFor("john-smith", listOf("E123456", "R123456", "B123456", "N123456")) + + assertThat(res.access.size, equalTo(4)) + assertThat(res, equalTo(userAccess())) + } + + private fun givenLimitedAccessResults() { + val personAccesses = listOf( + object : PersonAccess { + override val crn = "E123456" + override val exclusionMessage = "This person has an exclusion" + override val restrictionMessage = null + }, + object : PersonAccess { + override val crn = "R123456" + override val exclusionMessage = null + override val restrictionMessage = "This person has a restriction" + }, + object : PersonAccess { + override val crn = "B123456" + override val exclusionMessage = "This person has an exclusion" + override val restrictionMessage = "This person has a restriction" + }, + object : PersonAccess { + override val crn = "N123456" + override val exclusionMessage = null + override val restrictionMessage = null + } + ) + + whenever(uar.getAccessFor("john-smith", listOf("E123456", "R123456", "B123456", "N123456"))) + .thenReturn(personAccesses) + } + + private fun userAccess(): UserAccess = + UserAccess( + listOf( + CaseAccess("E123456", userExcluded = true, userRestricted = false, "This person has an exclusion"), + CaseAccess( + "R123456", + userExcluded = false, + userRestricted = true, + restrictionMessage = "This person has a restriction" + ), + CaseAccess( + "B123456", + userExcluded = true, + userRestricted = true, + "This person has an exclusion", + "This person has a restriction" + ), + CaseAccess("N123456", userExcluded = false, userRestricted = false) + ) + ) +} diff --git a/projects/refer-and-monitor-and-delius/build.gradle.kts b/projects/refer-and-monitor-and-delius/build.gradle.kts index 9302169931..a42cde7b0f 100644 --- a/projects/refer-and-monitor-and-delius/build.gradle.kts +++ b/projects/refer-and-monitor-and-delius/build.gradle.kts @@ -5,6 +5,7 @@ apply(plugin = "com.google.cloud.tools.jib") dependencies { implementation(project(":libs:audit")) implementation(project(":libs:commons")) + implementation(project(":libs:limited-access")) implementation(project(":libs:messaging")) implementation(project(":libs:oauth-client")) implementation(project(":libs:oauth-server")) diff --git a/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt b/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt index 50dadd6222..4e53158e33 100644 --- a/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt +++ b/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt @@ -20,6 +20,8 @@ import uk.gov.justice.digital.hmpps.data.generator.ProviderGenerator import uk.gov.justice.digital.hmpps.data.generator.ReferenceDataGenerator import uk.gov.justice.digital.hmpps.data.generator.SentenceGenerator import uk.gov.justice.digital.hmpps.data.generator.UserGenerator +import uk.gov.justice.digital.hmpps.entity.Exclusion +import uk.gov.justice.digital.hmpps.entity.Restriction import uk.gov.justice.digital.hmpps.integrations.delius.audit.BusinessInteractionCode import uk.gov.justice.digital.hmpps.integrations.delius.contact.ContactOutcomeRepository import uk.gov.justice.digital.hmpps.integrations.delius.contact.ContactRepository @@ -30,8 +32,6 @@ import uk.gov.justice.digital.hmpps.integrations.delius.event.entity.DisposalTyp import uk.gov.justice.digital.hmpps.integrations.delius.event.entity.EventRepository import uk.gov.justice.digital.hmpps.integrations.delius.event.entity.MainOffence import uk.gov.justice.digital.hmpps.integrations.delius.event.entity.Offence -import uk.gov.justice.digital.hmpps.integrations.delius.limitedaccess.entity.Exclusion -import uk.gov.justice.digital.hmpps.integrations.delius.limitedaccess.entity.Restriction import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.Disability import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.PersonAddressRepository import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.PersonDetailRepository diff --git a/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/LimitedAccessGenerator.kt b/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/LimitedAccessGenerator.kt index 0b4db5190a..bdda361e61 100644 --- a/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/LimitedAccessGenerator.kt +++ b/projects/refer-and-monitor-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/LimitedAccessGenerator.kt @@ -1,7 +1,9 @@ package uk.gov.justice.digital.hmpps.data.generator -import uk.gov.justice.digital.hmpps.integrations.delius.limitedaccess.entity.Exclusion -import uk.gov.justice.digital.hmpps.integrations.delius.limitedaccess.entity.Restriction +import uk.gov.justice.digital.hmpps.entity.Exclusion +import uk.gov.justice.digital.hmpps.entity.LimitedAccessPerson +import uk.gov.justice.digital.hmpps.entity.LimitedAccessUser +import uk.gov.justice.digital.hmpps.entity.Restriction import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.Person import uk.gov.justice.digital.hmpps.user.AuditUser import java.time.LocalDateTime @@ -15,12 +17,15 @@ object LimitedAccessGenerator { user: AuditUser = UserGenerator.LIMITED_ACCESS_USER, endDateTime: LocalDateTime? = null, id: Long = IdGenerator.getAndIncrement() - ) = Exclusion(person, user, endDateTime, id) + ) = Exclusion(person.limitedAccess(), user.limitedAccess(), endDateTime, id) fun generateRestriction( person: Person, user: AuditUser = UserGenerator.AUDIT_USER, endDateTime: LocalDateTime? = null, id: Long = IdGenerator.getAndIncrement() - ) = Restriction(person, user, endDateTime, id) + ) = Restriction(person.limitedAccess(), user.limitedAccess(), endDateTime, id) + + private fun Person.limitedAccess() = LimitedAccessPerson(crn, exclusionMessage, restrictionMessage, id) + private fun AuditUser.limitedAccess() = LimitedAccessUser(username, id) } diff --git a/projects/refer-and-monitor-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/UserResourceTest.kt b/projects/refer-and-monitor-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/UserResourceTest.kt index 30f5842b10..5430b14c80 100644 --- a/projects/refer-and-monitor-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/UserResourceTest.kt +++ b/projects/refer-and-monitor-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/UserResourceTest.kt @@ -18,15 +18,15 @@ import org.springframework.http.MediaType import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.request.MockMvcRequestBuilders import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import uk.gov.justice.digital.hmpps.api.model.CaseAccess import uk.gov.justice.digital.hmpps.api.model.CaseIdentifier import uk.gov.justice.digital.hmpps.api.model.ManagedCases import uk.gov.justice.digital.hmpps.api.model.Name -import uk.gov.justice.digital.hmpps.api.model.UserAccess import uk.gov.justice.digital.hmpps.api.model.UserDetail import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator import uk.gov.justice.digital.hmpps.data.generator.UserGenerator import uk.gov.justice.digital.hmpps.security.withOAuth2Token +import uk.gov.justice.digital.hmpps.service.CaseAccess +import uk.gov.justice.digital.hmpps.service.UserAccess @AutoConfigureMockMvc @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) diff --git a/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/CaseAccess.kt b/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/CaseAccess.kt deleted file mode 100644 index c33b56aa91..0000000000 --- a/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/CaseAccess.kt +++ /dev/null @@ -1,11 +0,0 @@ -package uk.gov.justice.digital.hmpps.api.model - -data class CaseAccess( - val crn: String, - val userExcluded: Boolean, - val userRestricted: Boolean, - val exclusionMessage: String? = null, - val restrictionMessage: String? = null -) - -data class UserAccess(val access: List) diff --git a/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/resource/UserResource.kt b/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/resource/UserResource.kt index 91bed904f1..c7472ef150 100644 --- a/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/resource/UserResource.kt +++ b/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/resource/UserResource.kt @@ -11,9 +11,9 @@ import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestMethod import org.springframework.web.bind.annotation.RestController import uk.gov.justice.digital.hmpps.api.model.ManagedCases -import uk.gov.justice.digital.hmpps.api.model.UserAccess import uk.gov.justice.digital.hmpps.api.model.UserDetail import uk.gov.justice.digital.hmpps.service.ManagerService +import uk.gov.justice.digital.hmpps.service.UserAccess import uk.gov.justice.digital.hmpps.service.UserService @Validated diff --git a/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/UserService.kt b/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/UserService.kt index 99116d9ddf..f42681a31f 100644 --- a/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/UserService.kt +++ b/projects/refer-and-monitor-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/UserService.kt @@ -2,38 +2,17 @@ package uk.gov.justice.digital.hmpps.service import org.springframework.ldap.core.LdapTemplate import org.springframework.stereotype.Service -import uk.gov.justice.digital.hmpps.api.model.CaseAccess import uk.gov.justice.digital.hmpps.api.model.Name -import uk.gov.justice.digital.hmpps.api.model.UserAccess import uk.gov.justice.digital.hmpps.api.model.UserDetail -import uk.gov.justice.digital.hmpps.integrations.delius.limitedaccess.entity.PersonAccess -import uk.gov.justice.digital.hmpps.integrations.delius.limitedaccess.entity.UserAccessRepository import uk.gov.justice.digital.hmpps.integrations.ldap.entity.LdapUserDetails import uk.gov.justice.digital.hmpps.ldap.findByUsername @Service -class UserService(private val uar: UserAccessRepository, private val ldapTemplate: LdapTemplate) { +class UserService(private val userAccessService: UserAccessService, private val ldapTemplate: LdapTemplate) { fun userDetails(username: String): UserDetail? = ldapTemplate.findByUsername(username)?.let { UserDetail(it.username, Name(it.forename, it.surname), it.email) } - fun userAccessFor(username: String, crns: List): UserAccess { - val limitations: Map> = uar.getAccessFor(username, crns).groupBy { it.crn } - return UserAccess(crns.map { limitations[it].combined(it) }) - } - - private fun List?.combined(crn: String): CaseAccess { - return if (this == null) { - CaseAccess(crn, userExcluded = false, userRestricted = false) - } else { - CaseAccess( - crn, - any { !it.exclusionMessage.isNullOrBlank() }, - any { !it.restrictionMessage.isNullOrBlank() }, - firstOrNull { !it.exclusionMessage.isNullOrBlank() }?.exclusionMessage, - firstOrNull { !it.restrictionMessage.isNullOrBlank() }?.restrictionMessage - ) - } - } + fun userAccessFor(username: String, crns: List) = userAccessService.userAccessFor(username, crns) } diff --git a/projects/workforce-allocations-to-delius/build.gradle.kts b/projects/workforce-allocations-to-delius/build.gradle.kts index f73bc3247b..c93e32f857 100644 --- a/projects/workforce-allocations-to-delius/build.gradle.kts +++ b/projects/workforce-allocations-to-delius/build.gradle.kts @@ -5,6 +5,7 @@ apply(plugin = "com.google.cloud.tools.jib") dependencies { implementation(project(":libs:audit")) implementation(project(":libs:commons")) + implementation(project(":libs:limited-access")) implementation(project(":libs:messaging")) implementation(project(":libs:oauth-client")) implementation(project(":libs:oauth-server")) diff --git a/projects/workforce-allocations-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/LimitedAccessDataLoader.kt b/projects/workforce-allocations-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/LimitedAccessDataLoader.kt index 35df0d3a01..e39bdd1187 100644 --- a/projects/workforce-allocations-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/LimitedAccessDataLoader.kt +++ b/projects/workforce-allocations-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/LimitedAccessDataLoader.kt @@ -8,9 +8,9 @@ import uk.gov.justice.digital.hmpps.data.generator.LimitedAccessGenerator.genera import uk.gov.justice.digital.hmpps.data.generator.LimitedAccessGenerator.generateRestriction import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator import uk.gov.justice.digital.hmpps.data.generator.UserGenerator +import uk.gov.justice.digital.hmpps.entity.Exclusion +import uk.gov.justice.digital.hmpps.entity.Restriction import uk.gov.justice.digital.hmpps.integrations.delius.person.PersonRepository -import uk.gov.justice.digital.hmpps.integrations.delius.user.access.entity.Exclusion -import uk.gov.justice.digital.hmpps.integrations.delius.user.access.entity.Restriction import uk.gov.justice.digital.hmpps.user.AuditUserRepository @Component diff --git a/projects/workforce-allocations-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/LimitedAccessGenerator.kt b/projects/workforce-allocations-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/LimitedAccessGenerator.kt index 97630cacdf..ed8fa13df4 100644 --- a/projects/workforce-allocations-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/LimitedAccessGenerator.kt +++ b/projects/workforce-allocations-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/LimitedAccessGenerator.kt @@ -1,8 +1,10 @@ package uk.gov.justice.digital.hmpps.data.generator +import uk.gov.justice.digital.hmpps.entity.Exclusion +import uk.gov.justice.digital.hmpps.entity.LimitedAccessPerson +import uk.gov.justice.digital.hmpps.entity.LimitedAccessUser +import uk.gov.justice.digital.hmpps.entity.Restriction import uk.gov.justice.digital.hmpps.integrations.delius.person.Person -import uk.gov.justice.digital.hmpps.integrations.delius.user.access.entity.Exclusion -import uk.gov.justice.digital.hmpps.integrations.delius.user.access.entity.Restriction import uk.gov.justice.digital.hmpps.user.AuditUser import java.time.LocalDateTime @@ -15,12 +17,15 @@ object LimitedAccessGenerator { person: Person = PersonGenerator.EXCLUSION, endDateTime: LocalDateTime? = null, id: Long = IdGenerator.getAndIncrement() - ) = Exclusion(id, person, user, endDateTime) + ) = Exclusion(person.limitedAccess(), user.limitedAccess(), endDateTime, id) fun generateRestriction( user: AuditUser = UserGenerator.AUDIT_USER, person: Person = PersonGenerator.RESTRICTION, endDateTime: LocalDateTime? = null, id: Long = IdGenerator.getAndIncrement() - ) = Restriction(id, person, user, endDateTime) + ) = Restriction(person.limitedAccess(), user.limitedAccess(), endDateTime, id) + + private fun Person.limitedAccess() = LimitedAccessPerson(crn, exclusionMessage, restrictionMessage, id) + private fun AuditUser.limitedAccess() = LimitedAccessUser(username, id) } diff --git a/projects/workforce-allocations-to-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/LimitedAccessIntegrationTest.kt b/projects/workforce-allocations-to-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/LimitedAccessIntegrationTest.kt index 45d8d09b2f..3c5aebde39 100644 --- a/projects/workforce-allocations-to-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/LimitedAccessIntegrationTest.kt +++ b/projects/workforce-allocations-to-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/LimitedAccessIntegrationTest.kt @@ -12,6 +12,7 @@ import org.springframework.boot.test.context.SpringBootTest import org.springframework.http.MediaType import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.request.MockMvcRequestBuilders +import uk.gov.justice.digital.hmpps.api.model.CaseAccess import uk.gov.justice.digital.hmpps.api.model.UserAccess import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator import uk.gov.justice.digital.hmpps.data.generator.UserGenerator @@ -32,7 +33,7 @@ class LimitedAccessIntegrationTest { @Test fun `limited access controls are correctly returned`() { val res = mockMvc.perform( - MockMvcRequestBuilders.post("/user/${UserGenerator.LIMITED_ACCESS_USER.username}/access-controls") + MockMvcRequestBuilders.post("/users/${UserGenerator.LIMITED_ACCESS_USER.username}/access") .withOAuth2Token(wireMockserver) .contentType(MediaType.APPLICATION_JSON) .content( @@ -47,11 +48,12 @@ class LimitedAccessIntegrationTest { ) ).andReturn().response.contentAsString - val result = objectMapper.readValue>(res) + val result = objectMapper.readValue(res) assertThat( - result[PersonGenerator.EXCLUSION.crn], + result.access.first { it.crn == PersonGenerator.EXCLUSION.crn }, equalTo( - UserAccess( + CaseAccess( + PersonGenerator.EXCLUSION.crn, userExcluded = true, userRestricted = false, exclusionMessage = PersonGenerator.EXCLUSION.exclusionMessage @@ -59,9 +61,10 @@ class LimitedAccessIntegrationTest { ) ) assertThat( - result[PersonGenerator.RESTRICTION.crn], + result.access.first { it.crn == PersonGenerator.RESTRICTION.crn }, equalTo( - UserAccess( + CaseAccess( + PersonGenerator.RESTRICTION.crn, userExcluded = false, userRestricted = true, restrictionMessage = PersonGenerator.RESTRICTION.restrictionMessage @@ -69,18 +72,20 @@ class LimitedAccessIntegrationTest { ) ) assertThat( - result[PersonGenerator.DEFAULT.crn], + result.access.first { it.crn == PersonGenerator.DEFAULT.crn }, equalTo( - UserAccess( + CaseAccess( + PersonGenerator.DEFAULT.crn, userExcluded = false, userRestricted = false ) ) ) assertThat( - result[PersonGenerator.RESTRICTION_EXCLUSION.crn], + result.access.first { it.crn == PersonGenerator.RESTRICTION_EXCLUSION.crn }, equalTo( - UserAccess( + CaseAccess( + PersonGenerator.RESTRICTION_EXCLUSION.crn, userExcluded = true, userRestricted = true, exclusionMessage = PersonGenerator.RESTRICTION_EXCLUSION.exclusionMessage, @@ -93,7 +98,7 @@ class LimitedAccessIntegrationTest { @Test fun `limited access controls do not prevent legitimate access`() { val res = mockMvc.perform( - MockMvcRequestBuilders.post("/user/${UserGenerator.AUDIT_USER.username}/access-controls") + MockMvcRequestBuilders.post("/users/${UserGenerator.AUDIT_USER.username}/access") .withOAuth2Token(wireMockserver) .contentType(MediaType.APPLICATION_JSON) .content( @@ -107,18 +112,18 @@ class LimitedAccessIntegrationTest { ) ).andReturn().response.contentAsString - val result = objectMapper.readValue>(res) + val result = objectMapper.readValue(res) assertThat( - result[PersonGenerator.EXCLUSION.crn], - equalTo(UserAccess.NO_ACCESS_LIMITATIONS) + result.access.first { it.crn == PersonGenerator.EXCLUSION.crn }, + equalTo(CaseAccess(PersonGenerator.EXCLUSION.crn, userExcluded = false, userRestricted = false)) ) assertThat( - result[PersonGenerator.RESTRICTION.crn], - equalTo(UserAccess.NO_ACCESS_LIMITATIONS) + result.access.first { it.crn == PersonGenerator.RESTRICTION.crn }, + equalTo(CaseAccess(PersonGenerator.RESTRICTION.crn, userExcluded = false, userRestricted = false)) ) assertThat( - result[PersonGenerator.DEFAULT.crn], - equalTo(UserAccess.NO_ACCESS_LIMITATIONS) + result.access.first { it.crn == PersonGenerator.DEFAULT.crn }, + equalTo(CaseAccess(PersonGenerator.DEFAULT.crn, userExcluded = false, userRestricted = false)) ) } } diff --git a/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/UserAccess.kt b/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/UserAccess.kt index 2cbaf73679..e1a95462ef 100644 --- a/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/UserAccess.kt +++ b/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/UserAccess.kt @@ -1,12 +1,11 @@ package uk.gov.justice.digital.hmpps.api.model -data class UserAccess( +data class UserAccess(val access: List) + +data class CaseAccess( + val crn: String, val userExcluded: Boolean, val userRestricted: Boolean, val exclusionMessage: String? = null, val restrictionMessage: String? = null -) { - companion object { - val NO_ACCESS_LIMITATIONS = UserAccess(userExcluded = false, userRestricted = false) - } -} +) diff --git a/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/resource/UserResource.kt b/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/resource/UserResource.kt index a84ad73042..9f79f07557 100644 --- a/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/resource/UserResource.kt +++ b/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/resource/UserResource.kt @@ -1,6 +1,7 @@ package uk.gov.justice.digital.hmpps.api.resource import io.swagger.v3.oas.annotations.Operation +import jakarta.validation.constraints.Size import org.springframework.security.access.prepost.PreAuthorize import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestBody @@ -10,7 +11,7 @@ import org.springframework.web.bind.annotation.RestController import uk.gov.justice.digital.hmpps.service.UserAccessService @RestController -@RequestMapping("/user") +@RequestMapping("/users") class UserResource(private val userAccessService: UserAccessService) { @PreAuthorize("hasAnyRole('ROLE_ALLOCATION_CONTEXT', 'ROLE_WORKFORCE_DOCUMENT')") @@ -26,7 +27,9 @@ class UserResource(private val userAccessService: UserAccessService) { has a restriction in place """ ) - @RequestMapping("/{username}/access-controls", method = [RequestMethod.GET, RequestMethod.POST]) - fun userAccessCheck(@PathVariable username: String, @RequestBody crns: List) = - userAccessService.userAccessFor(username, crns) + @RequestMapping("/{username}/access", method = [RequestMethod.GET, RequestMethod.POST]) + fun userAccessCheck( + @PathVariable username: String, + @Size(min = 1, max = 500, message = "Please provide between 1 and 500 crns") @RequestBody crns: List + ) = userAccessService.userAccessFor(username, crns) } diff --git a/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/user/access/UserAccessRepository.kt b/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/user/access/UserAccessRepository.kt deleted file mode 100644 index 430a43a03d..0000000000 --- a/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/user/access/UserAccessRepository.kt +++ /dev/null @@ -1,30 +0,0 @@ -package uk.gov.justice.digital.hmpps.integrations.delius.user.access - -import org.springframework.data.jpa.repository.JpaRepository -import org.springframework.data.jpa.repository.Query -import uk.gov.justice.digital.hmpps.user.AuditUser - -interface UserAccessRepository : JpaRepository { - @Query( - """ - select new uk.gov.justice.digital.hmpps.integrations.delius.user.access.UserPersonAccess(p.crn, p.exclusionMessage, '') - from Person p where p.crn in :crns - and exists (select e from Exclusion e where upper(e.user.username) = upper(:username) and e.person.id = p.id and (e.end is null or e.end > current_date )) - union - select new uk.gov.justice.digital.hmpps.integrations.delius.user.access.UserPersonAccess(p.crn, '', p.restrictionMessage) - from Person p where p.crn in :crns - and exists (select r from Restriction r where r.person.id = p.id and (r.end is null or r.end > current_date )) - and not exists (select r from Restriction r where upper(r.user.username) = upper(:username) and r.person.id = p.id and (r.end is null or r.end > current_date )) - """ - ) - fun getAccessFor(username: String, crns: List): List -} - -data class UserPersonAccess( - val crn: String, - val exclusionMessage: String?, - val restrictionMessage: String? -) { - fun isExcluded(): Boolean = !exclusionMessage.isNullOrBlank() - fun isRestricted(): Boolean = !restrictionMessage.isNullOrBlank() -} diff --git a/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/user/access/entity/LimitedAccess.kt b/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/user/access/entity/LimitedAccess.kt deleted file mode 100644 index 0cc0716506..0000000000 --- a/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/user/access/entity/LimitedAccess.kt +++ /dev/null @@ -1,49 +0,0 @@ -package uk.gov.justice.digital.hmpps.integrations.delius.user.access.entity - -import jakarta.persistence.Column -import jakarta.persistence.Entity -import jakarta.persistence.Id -import jakarta.persistence.JoinColumn -import jakarta.persistence.ManyToOne -import org.hibernate.annotations.Immutable -import uk.gov.justice.digital.hmpps.integrations.delius.person.Person -import uk.gov.justice.digital.hmpps.user.AuditUser -import java.time.LocalDateTime - -@Immutable -@Entity -class Exclusion( - @Id - @Column(name = "exclusion_id") - val id: Long, - - @ManyToOne - @JoinColumn(name = "offender_id") - val person: Person, - - @ManyToOne - @JoinColumn(name = "user_id") - val user: AuditUser, - - @Column(name = "exclusion_end_time") - val end: LocalDateTime? = null -) - -@Immutable -@Entity -class Restriction( - @Id - @Column(name = "restriction_id") - val id: Long, - - @ManyToOne - @JoinColumn(name = "offender_id") - val person: Person, - - @ManyToOne - @JoinColumn(name = "user_id") - val user: AuditUser, - - @Column(name = "restriction_end_time") - val end: LocalDateTime? = null -) diff --git a/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/UserAccessService.kt b/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/UserAccessService.kt deleted file mode 100644 index b0541caffd..0000000000 --- a/projects/workforce-allocations-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/UserAccessService.kt +++ /dev/null @@ -1,27 +0,0 @@ -package uk.gov.justice.digital.hmpps.service - -import org.springframework.stereotype.Service -import uk.gov.justice.digital.hmpps.api.model.UserAccess -import uk.gov.justice.digital.hmpps.integrations.delius.user.access.UserAccessRepository -import uk.gov.justice.digital.hmpps.integrations.delius.user.access.UserPersonAccess - -@Service -class UserAccessService(private val uar: UserAccessRepository) { - fun userAccessFor(username: String, crns: List): Map { - val limitations: Map> = uar.getAccessFor(username, crns).groupBy { it.crn } - return crns.associateWith { limitations[it].combined() } - } - - private fun List?.combined(): UserAccess { - return if (this == null) { - UserAccess.NO_ACCESS_LIMITATIONS - } else { - UserAccess( - any { it.isExcluded() }, - any { it.isRestricted() }, - firstOrNull { it.isExcluded() }?.exclusionMessage, - firstOrNull { it.isRestricted() }?.restrictionMessage - ) - } - } -} diff --git a/settings.gradle.kts b/settings.gradle.kts index 70f6e4d158..c20dd7c683 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -34,7 +34,8 @@ include( "libs:dev-tools", "libs:messaging", "libs:oauth-client", - "libs:oauth-server" + "libs:oauth-server", + "libs:limited-access" ) // load children from the "projects" directory (and drop the prefix)