Skip to content

Commit

Permalink
PI-2887: new api to get user roles (#4738)
Browse files Browse the repository at this point in the history
  • Loading branch information
achimber-moj authored Mar 3, 2025
1 parent 6729f22 commit e589eb7
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,16 @@ cn: peter-parker
sn: Parker
givenname: Peter
mail: [email protected]
telephoneNumber: 07321165373
telephoneNumber: 07321165373

dn: cn=MAABT001,cn=peter-parker,ou=Users,dc=moj,dc=com
objectclass: NDRoleAssociation
objectclass: alias
cn: MAABT001
aliasedObjectName: cn=MAABT001,cn=NDRoleAssociation,ou=Users,dc=moj,dc=com

dn: cn=APBT001,cn=peter-parker,ou=Users,dc=moj,dc=com
objectclass: NDRoleAssociation
objectclass: alias
cn: APBT001
aliasedObjectName: cn=APBT001,cn=NDRoleAssociation,ou=Users,dc=moj,dc=com
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package uk.gov.justice.digital.hmpps

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
import uk.gov.justice.digital.hmpps.api.model.user.UserDetails
import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.contentAsJson
import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.withToken

@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserRoleIntegrationTest {
@Autowired
lateinit var mockMvc: MockMvc

@Test
fun `unauthorized status returned`() {
mockMvc
.perform(MockMvcRequestBuilders.get("/caseload/user/peter-parker"))
.andExpect(MockMvcResultMatchers.status().isUnauthorized)
}

@Test
fun `get roles for user`() {
val response = mockMvc
.perform(MockMvcRequestBuilders.get("/user/peter-parker").withToken())
.andExpect(MockMvcResultMatchers.status().isOk)
.andReturn().response.contentAsJson<UserDetails>()

assertEquals(listOf("APBT001", "MAABT001"), response.roles)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ import jakarta.validation.constraints.Size
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.*
import uk.gov.justice.digital.hmpps.service.UserAccessService
import uk.gov.justice.digital.hmpps.service.UserService

@RestController
@Tag(name = "User access")
@PreAuthorize("hasRole('PROBATION_API__MANAGE_A_SUPERVISION__CASE_DETAIL')")
class UserAccessController(private val userAccessService: UserAccessService) {
class UserAccessController(
private val userAccessService: UserAccessService,
private val userService: UserService
) {
@GetMapping("/user/{username}/access/{crn}")
fun checkAccess(@PathVariable username: String, @PathVariable crn: String) =
userAccessService.caseAccessFor(username, crn)
Expand All @@ -20,4 +24,7 @@ class UserAccessController(private val userAccessService: UserAccessService) {
@Size(min = 1, max = 500, message = "Please provide between 1 and 500 crns")
@RequestBody crns: List<String>
) = userAccessService.userAccessFor(username, crns)

@GetMapping("/user/{username}")
fun getUserRoles(@PathVariable username: String) = userService.getUserDetails(username)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package uk.gov.justice.digital.hmpps.api.model.user

data class UserDetails(
val userId: Long,
val username: String,
val firstName: String,
val surname: String,
val email: String?,
val enabled: Boolean,
val roles: List<String>
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import org.springframework.ldap.odm.annotations.Attribute
import org.springframework.ldap.odm.annotations.DnAttribute
import org.springframework.ldap.odm.annotations.Entry
import org.springframework.ldap.odm.annotations.Id
import org.springframework.ldap.odm.annotations.Transient
import java.time.LocalDate
import java.time.LocalDate.now
import java.time.format.DateTimeFormatter.ofPattern
import javax.naming.Name

@Entry(objectClasses = ["inetOrgPerson", "top"])
Expand All @@ -15,9 +19,24 @@ class LdapUser(
@DnAttribute(value = "cn", index = 0)
val username: String,

@Attribute(name = "givenName")
val forename: String,

@Attribute(name = "sn")
val surname: String,

@Attribute(name = "mail")
val email: String?,

@Attribute(name = "telephoneNumber")
val telephone: String?
)
val telephone: String?,

@Attribute(name = "endDate")
val endDate: String?,

@Transient
var roles: List<String>
) {
val enabled: Boolean
get() = endDate == null || LocalDate.parse(endDate.substring(0, 8), ofPattern("yyyyMMdd")).isAfter(now())
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ interface UserRepository : JpaRepository<User, Long> {
"""
)
fun findByUsername(username: String): User?

@Query("select u from User u where upper(u.username) = upper(:username)")
fun findUserByUsername(username: String): User?
}

fun UserRepository.getUser(username: String) =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,53 @@
package uk.gov.justice.digital.hmpps.service

import org.springframework.data.domain.Pageable
import org.springframework.ldap.core.AttributesMapper
import org.springframework.ldap.core.LdapTemplate
import org.springframework.ldap.query.LdapQueryBuilder.query
import org.springframework.ldap.query.SearchScope
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import uk.gov.justice.digital.hmpps.api.model.Name
import uk.gov.justice.digital.hmpps.api.model.overview.Appointment
import uk.gov.justice.digital.hmpps.api.model.user.*
import uk.gov.justice.digital.hmpps.api.model.user.Staff
import uk.gov.justice.digital.hmpps.api.model.user.Team
import uk.gov.justice.digital.hmpps.exception.NotFoundException
import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LdapUser
import uk.gov.justice.digital.hmpps.integrations.delius.user.entity.*
import uk.gov.justice.digital.hmpps.ldap.findByUsername

@Service
class UserService(
private val userRepository: UserRepository,
private val caseloadRepository: CaseloadRepository,
private val staffRepository: StaffRepository,
private val teamRepository: TeamRepository,
private val userAccessService: UserAccessService
private val userAccessService: UserAccessService,
private val ldapTemplate: LdapTemplate
) {
fun getUserDetails(username: String) = ldapTemplate.findByUsername<LdapUser>(username)?.toUserDetails()

private fun LdapUser.toUserDetails() = userRepository.findUserByUsername(username)?.let { toUserDetails(it.id) }
?: throw NotFoundException("User entity", "username", username)

private fun LdapUser.toUserDetails(userId: Long) = UserDetails(
userId = userId,
username = username,
firstName = forename,
surname = surname,
email = email,
enabled = enabled,
roles = getUserRoles(dn)
)

private fun getUserRoles(name: javax.naming.Name): List<String> = ldapTemplate.search(
query()
.base(name)
.searchScope(SearchScope.ONELEVEL)
.filter("(|(objectclass=NDRole)(objectclass=NDRoleAssociation))"),
AttributesMapper { it["cn"].get().toString() }
)

@Transactional
fun getUserCaseload(username: String, pageable: Pageable): StaffCaseload {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ server.shutdown: immediate
spring:
datasource.url: jdbc:h2:file:./dev;MODE=Oracle;DEFAULT_NULL_ORDERING=HIGH;AUTO_SERVER=true;AUTO_SERVER_PORT=9092
jpa.hibernate.ddl-auto: create-drop
ldap.embedded.base-dn: ${spring.ldap.base}
ldap.embedded:
base-dn: ${spring.ldap.base}
validation.enabled: false

messaging.producer.topic: domain-events

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.whenever
import uk.gov.justice.digital.hmpps.service.CaseAccess
import uk.gov.justice.digital.hmpps.service.UserAccessService
import uk.gov.justice.digital.hmpps.service.UserService

@ExtendWith(MockitoExtension::class)
internal class UserControllerTest {
@Mock
lateinit var userAccessService: UserAccessService

@Mock
lateinit var userService: UserService

@InjectMocks
lateinit var userAccessController: UserAccessController

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.whenever
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.Pageable
import org.springframework.ldap.core.LdapTemplate
import uk.gov.justice.digital.hmpps.data.generator.ContactGenerator.DEFAULT_PROVIDER
import uk.gov.justice.digital.hmpps.data.generator.ContactGenerator.DEFAULT_STAFF
import uk.gov.justice.digital.hmpps.data.generator.ContactGenerator.DEFAULT_TEAM
Expand Down Expand Up @@ -41,6 +42,9 @@ internal class UserServiceTest {
@Mock
lateinit var userAccessService: UserAccessService

@Mock
lateinit var ldapTemplate: LdapTemplate

@InjectMocks
lateinit var service: UserService

Expand Down

0 comments on commit e589eb7

Please sign in to comment.