generated from ministryofjustice/template-repository
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
f153cc3
commit 518599d
Showing
11 changed files
with
256 additions
and
37 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
libs/commons/src/main/kotlin/uk/gov/justice/digital/hmpps/ldap/DeliusRole.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package uk.gov.justice.digital.hmpps.ldap | ||
|
||
interface DeliusRole { | ||
val description: String | ||
val mappedRole: String | ||
val name: String | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,22 +3,31 @@ package uk.gov.justice.digital.hmpps.ldap | |
import org.hamcrest.MatcherAssert.assertThat | ||
import org.hamcrest.Matchers.equalTo | ||
import org.junit.jupiter.api.Test | ||
import org.junit.jupiter.api.assertThrows | ||
import org.junit.jupiter.api.extension.ExtendWith | ||
import org.mockito.Mock | ||
import org.mockito.junit.jupiter.MockitoExtension | ||
import org.mockito.kotlin.any | ||
import org.mockito.kotlin.argumentCaptor | ||
import org.mockito.kotlin.eq | ||
import org.mockito.kotlin.verify | ||
import org.mockito.kotlin.whenever | ||
import org.springframework.ldap.core.AttributesMapper | ||
import org.springframework.ldap.core.DirContextOperations | ||
import org.springframework.ldap.core.LdapTemplate | ||
import uk.gov.justice.digital.hmpps.exception.NotFoundException | ||
import uk.gov.justice.digital.hmpps.ldap.entity.LdapUser | ||
import javax.naming.directory.Attributes | ||
import javax.naming.ldap.LdapName | ||
|
||
@ExtendWith(MockitoExtension::class) | ||
class LdapTemplateExtensionsTest { | ||
@Mock | ||
private lateinit var ldapTemplate: LdapTemplate | ||
|
||
@Mock | ||
private lateinit var dirContextOperations: DirContextOperations | ||
|
||
@Test | ||
fun `find by username`() { | ||
val expected = LdapUser(LdapName("cn=test,ou=Users"), "test", "[email protected]") | ||
|
@@ -38,4 +47,65 @@ class LdapTemplateExtensionsTest { | |
|
||
assertThat(email, equalTo("[email protected]")) | ||
} | ||
|
||
@Test | ||
fun `add role successfully`() { | ||
whenever(ldapTemplate.lookupContext(any<LdapName>())) | ||
.thenReturn(dirContextOperations) | ||
whenever(dirContextOperations.toString()) | ||
.thenReturn("cn=ROLE1,cn=ndRoleCatalogue,ou=Users,dc=moj,dc=com") | ||
|
||
ldapTemplate.addRole( | ||
"john-smith", | ||
object : DeliusRole { | ||
override val description = "Role One Description" | ||
override val mappedRole = "MAPPED_ROLE_ONE" | ||
override val name = "ROLE1" | ||
} | ||
) | ||
|
||
val nameCapture = argumentCaptor<LdapName>() | ||
val attributeCapture = argumentCaptor<Attributes>() | ||
verify(ldapTemplate).rebind(nameCapture.capture(), eq(null), attributeCapture.capture()) | ||
|
||
assertThat(nameCapture.firstValue.toString(), equalTo("cn=ROLE1,cn=john-smith,ou=Users")) | ||
assertThat(attributeCapture.firstValue["cn"].toString(), equalTo("cn: ROLE1")) | ||
assertThat(attributeCapture.firstValue["objectclass"].toString(), equalTo("objectclass: NDRoleAssociation, Alias, top")) | ||
} | ||
|
||
@Test | ||
fun `unable to add unknown role`() { | ||
whenever(ldapTemplate.lookupContext(any<LdapName>())) | ||
.thenReturn(null) | ||
|
||
val res = assertThrows<NotFoundException> { | ||
ldapTemplate.addRole( | ||
"john-smith", | ||
object : DeliusRole { | ||
override val description = "Unknown Description" | ||
override val mappedRole = "MAPPED_ROLE_UKN" | ||
override val name = "UNKNOWN" | ||
} | ||
) | ||
} | ||
|
||
assertThat(res.message, equalTo("NDeliusRole of UNKNOWN not found")) | ||
} | ||
|
||
@Test | ||
fun `remove role successfully`() { | ||
ldapTemplate.removeRole( | ||
"john-smith", | ||
object : DeliusRole { | ||
override val description = "Role One Description" | ||
override val mappedRole = "MAPPED_ROLE_ONE" | ||
override val name = "ROLE1" | ||
} | ||
) | ||
|
||
val nameCapture = argumentCaptor<LdapName>() | ||
verify(ldapTemplate).unbind(nameCapture.capture()) | ||
|
||
assertThat(nameCapture.firstValue.toString(), equalTo("cn=ROLE1,cn=john-smith,ou=Users")) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,4 +20,22 @@ objectclass: top | |
objectclass: inetOrgPerson | ||
cn: bob-smith | ||
sn: Staff | ||
mail: [email protected] | ||
mail: [email protected] | ||
|
||
dn: cn=ndRoleCatalogue,ou=Users,dc=moj,dc=com | ||
description: Role Catalogue | ||
objectclass: top | ||
cn: ndRoleCatalogue | ||
|
||
dn: cn=LHDCBT002,cn=ndRoleCatalogue,ou=Users,dc=moj,dc=com | ||
description: Digital Licence Create | ||
Level1: TRUE | ||
Level2: FALSE | ||
Level3: FALSE | ||
UIBusinessInteractionCollection: SEBI200 | ||
UIBusinessInteractionCollection: SEBI203 | ||
UIBusinessInteractionCollection: SEBI201 | ||
UIBusinessInteractionCollection: SEBI202 | ||
objectClass: NDRole | ||
objectClass: top | ||
cn: LHDCBT002 |
2 changes: 1 addition & 1 deletion
2
...ary-a-licence-and-delius/src/dev/resources/simulations/__files/hmpps-auth-token-body.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
...delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/UserRoleIntegrationTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package uk.gov.justice.digital.hmpps | ||
|
||
import com.github.tomakehurst.wiremock.WireMockServer | ||
import org.hamcrest.MatcherAssert.assertThat | ||
import org.hamcrest.Matchers.equalTo | ||
import org.junit.jupiter.api.MethodOrderer | ||
import org.junit.jupiter.api.Order | ||
import org.junit.jupiter.api.Test | ||
import org.junit.jupiter.api.TestMethodOrder | ||
import org.junit.jupiter.api.assertThrows | ||
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.ldap.NameNotFoundException | ||
import org.springframework.ldap.core.LdapTemplate | ||
import org.springframework.ldap.support.LdapNameBuilder | ||
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.DeliusRole | ||
import uk.gov.justice.digital.hmpps.security.withOAuth2Token | ||
|
||
@AutoConfigureMockMvc | ||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) | ||
@TestMethodOrder(MethodOrderer.OrderAnnotation::class) | ||
internal class UserRoleIntegrationTest { | ||
@Autowired | ||
lateinit var mockMvc: MockMvc | ||
|
||
@Autowired | ||
lateinit var wireMockServer: WireMockServer | ||
|
||
@Autowired | ||
lateinit var ldapTemplate: LdapTemplate | ||
|
||
@Order(1) | ||
@Test | ||
fun `successfully updates ldap role`() { | ||
mockMvc.perform( | ||
MockMvcRequestBuilders.put("/users/john-smith/roles") | ||
.withOAuth2Token(wireMockServer) | ||
).andExpect(status().is2xxSuccessful).andReturn() | ||
|
||
val res = ldapTemplate.lookupContext( | ||
LdapNameBuilder.newInstance("ou=Users") | ||
.add("cn", "john-smith") | ||
.add("cn", DeliusRole.LHDCBT002.name) | ||
.build() | ||
) | ||
assertThat(res.dn.toString(), equalTo("cn=LHDCBT002,cn=john-smith,ou=Users")) | ||
} | ||
|
||
@Order(2) | ||
@Test | ||
fun `successfully removes ldap role`() { | ||
mockMvc.perform( | ||
MockMvcRequestBuilders.delete("/users/john-smith/roles") | ||
.withOAuth2Token(wireMockServer) | ||
).andExpect(status().is2xxSuccessful).andReturn() | ||
|
||
val res = assertThrows<NameNotFoundException> { | ||
ldapTemplate.lookupContext( | ||
LdapNameBuilder.newInstance("ou=Users") | ||
.add("cn", "john-smith") | ||
.add("cn", DeliusRole.LHDCBT002.name) | ||
.build() | ||
) | ||
} | ||
assertThat(res.message, equalTo("[LDAP: error code 32 - Unable to perform the search because base entry 'cn=LHDCBT002,cn=john-smith,ou=Users,dc=moj,dc=com' does not exist in the server.]")) | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
...a-licence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/DeliusRole.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package uk.gov.justice.digital.hmpps.api.model | ||
|
||
enum class DeliusRole( | ||
override val description: String, | ||
override val mappedRole: String | ||
) : uk.gov.justice.digital.hmpps.ldap.DeliusRole { | ||
LHDCBT002("Digital Licence Create", "CVL_DLC"); | ||
|
||
companion object { | ||
fun from(role: String): DeliusRole? = entries.firstOrNull { it.mappedRole == role.uppercase() } | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
...ence-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/resource/UserResource.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package uk.gov.justice.digital.hmpps.api.resource | ||
|
||
import org.springframework.ldap.core.LdapTemplate | ||
import org.springframework.security.access.prepost.PreAuthorize | ||
import org.springframework.web.bind.annotation.DeleteMapping | ||
import org.springframework.web.bind.annotation.PathVariable | ||
import org.springframework.web.bind.annotation.PutMapping | ||
import org.springframework.web.bind.annotation.RequestMapping | ||
import org.springframework.web.bind.annotation.RestController | ||
import uk.gov.justice.digital.hmpps.api.model.DeliusRole | ||
import uk.gov.justice.digital.hmpps.ldap.addRole | ||
import uk.gov.justice.digital.hmpps.ldap.removeRole | ||
|
||
@RestController | ||
@RequestMapping("users") | ||
class UserResource(private val ldapTemplate: LdapTemplate) { | ||
@PreAuthorize("hasRole('PROBATION_API__CVL__USER_ROLES')") | ||
@PutMapping(value = ["/{username}/roles"]) | ||
fun addRole(@PathVariable username: String) = | ||
ldapTemplate.addRole(username, DeliusRole.LHDCBT002) | ||
|
||
@PreAuthorize("hasRole('PROBATION_API__CVL__USER_ROLES')") | ||
@DeleteMapping(value = ["/{username}/roles"]) | ||
fun removeRole(@PathVariable username: String) = | ||
ldapTemplate.removeRole(username, DeliusRole.LHDCBT002) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 4 additions & 29 deletions
33
...pathfinder-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/UserService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,15 @@ | ||
package uk.gov.justice.digital.hmpps.service | ||
|
||
import org.springframework.ldap.core.LdapTemplate | ||
import org.springframework.ldap.support.LdapNameBuilder | ||
import org.springframework.stereotype.Service | ||
import uk.gov.justice.digital.hmpps.exception.NotFoundException | ||
import uk.gov.justice.digital.hmpps.ldap.addRole | ||
import uk.gov.justice.digital.hmpps.ldap.removeRole | ||
import uk.gov.justice.digital.hmpps.model.DeliusRole | ||
import javax.naming.directory.Attributes | ||
import javax.naming.directory.BasicAttribute | ||
import javax.naming.directory.BasicAttributes | ||
|
||
@Service | ||
class UserService(private val ldapTemplate: LdapTemplate) { | ||
private val ldapBase = "ou=Users" | ||
|
||
fun addRole(username: String, role: DeliusRole) { | ||
val roleContext = ldapTemplate.lookupContext(role.context()) | ||
?: throw NotFoundException("NDeliusRole of ${role.name} not found") | ||
val attributes: Attributes = BasicAttributes(true).apply { | ||
put(roleContext.asAttribute("aliasedObjectName")) | ||
put(role.name.asAttribute("cn")) | ||
put(listOf("NDRoleAssociation", "Alias", "top").asAttribute("objectclass")) | ||
} | ||
val userRole = role.context(username) | ||
ldapTemplate.rebind(userRole, null, attributes) | ||
} | ||
fun addRole(username: String, role: DeliusRole) = ldapTemplate.addRole(username, role) | ||
|
||
fun removeRole(username: String, role: DeliusRole) = | ||
ldapTemplate.unbind(role.context(username)) | ||
|
||
private fun DeliusRole.context(username: String? = null) = | ||
LdapNameBuilder.newInstance(ldapBase) | ||
.add("cn", username ?: "ndRoleCatalogue") | ||
.add("cn", name) | ||
.build() | ||
|
||
fun Any.asAttribute(key: String) = BasicAttribute(key, this.toString()) | ||
fun List<Any>.asAttribute(key: String): BasicAttribute = | ||
BasicAttribute(key).apply { forEach(this::add) } | ||
fun removeRole(username: String, role: DeliusRole) = ldapTemplate.removeRole(username, role) | ||
} |