From 595e223b5280ccba249ca6c5c735bfe72e325417 Mon Sep 17 00:00:00 2001 From: Marcus Aspin Date: Wed, 9 Oct 2024 14:16:07 +0000 Subject: [PATCH] PI-2548 Add endpoints to add/remove IMS roles --- projects/ims-and-delius/build.gradle.kts | 2 + projects/ims-and-delius/deploy/values-dev.yml | 2 - .../ims-and-delius/deploy/values-preprod.yml | 2 - .../ims-and-delius/deploy/values-prod.yml | 2 - projects/ims-and-delius/deploy/values.yaml | 3 ++ .../justice/digital/hmpps/IntegrationTest.kt | 38 +++++++++++++++---- .../digital/hmpps/controller/ApiController.kt | 17 --------- .../hmpps/controller/UserController.kt | 29 ++++++++++++++ .../src/main/resources/application.yml | 9 ++++- .../src/main/resources/schema.ldif | 24 ++++++++++++ 10 files changed, 96 insertions(+), 32 deletions(-) delete mode 100644 projects/ims-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/ApiController.kt create mode 100644 projects/ims-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/UserController.kt create mode 100644 projects/ims-and-delius/src/main/resources/schema.ldif diff --git a/projects/ims-and-delius/build.gradle.kts b/projects/ims-and-delius/build.gradle.kts index a089a2f210..b4cf02352a 100644 --- a/projects/ims-and-delius/build.gradle.kts +++ b/projects/ims-and-delius/build.gradle.kts @@ -9,6 +9,7 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-actuator") implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("org.springframework.boot:spring-boot-starter-data-ldap") implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-validation") implementation("org.springframework.boot:spring-boot-starter-web") @@ -18,6 +19,7 @@ dependencies { implementation(libs.springdoc) dev(project(":libs:dev-tools")) + dev("com.unboundid:unboundid-ldapsdk") dev("com.h2database:h2") dev("org.testcontainers:oracle-xe") diff --git a/projects/ims-and-delius/deploy/values-dev.yml b/projects/ims-and-delius/deploy/values-dev.yml index 0e8ec7f252..4dea140f33 100644 --- a/projects/ims-and-delius/deploy/values-dev.yml +++ b/projects/ims-and-delius/deploy/values-dev.yml @@ -1,5 +1,3 @@ -enabled: false # TODO set this to true when you're ready to deploy your service - generic-service: ingress: host: ims-and-delius-dev.hmpps.service.justice.gov.uk diff --git a/projects/ims-and-delius/deploy/values-preprod.yml b/projects/ims-and-delius/deploy/values-preprod.yml index 1e059a3c11..8e59d9a67a 100644 --- a/projects/ims-and-delius/deploy/values-preprod.yml +++ b/projects/ims-and-delius/deploy/values-preprod.yml @@ -1,5 +1,3 @@ -enabled: false # TODO set this to true when you're ready to deploy your service - generic-service: ingress: host: ims-and-delius-preprod.hmpps.service.justice.gov.uk diff --git a/projects/ims-and-delius/deploy/values-prod.yml b/projects/ims-and-delius/deploy/values-prod.yml index efad63f78f..545f9e4f06 100644 --- a/projects/ims-and-delius/deploy/values-prod.yml +++ b/projects/ims-and-delius/deploy/values-prod.yml @@ -1,5 +1,3 @@ -enabled: false # TODO set this to true when you're ready to deploy your service - generic-service: ingress: host: ims-and-delius.hmpps.service.justice.gov.uk diff --git a/projects/ims-and-delius/deploy/values.yaml b/projects/ims-and-delius/deploy/values.yaml index 7416bea0a2..1eee0dff56 100644 --- a/projects/ims-and-delius/deploy/values.yaml +++ b/projects/ims-and-delius/deploy/values.yaml @@ -12,6 +12,9 @@ generic-service: namespace_secrets: common: SPRING_DATASOURCE_URL: DB_URL + SPRING_LDAP_URLS: LDAP_URL + SPRING_LDAP_USERNAME: LDAP_USERNAME + SPRING_LDAP_PASSWORD: LDAP_PASSWORD ims-and-delius-database: SPRING_DATASOURCE_USERNAME: DB_USERNAME SPRING_DATASOURCE_PASSWORD: DB_PASSWORD diff --git a/projects/ims-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt b/projects/ims-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt index d9006c74c5..ad91e8b9fe 100644 --- a/projects/ims-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt +++ b/projects/ims-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt @@ -1,30 +1,52 @@ package uk.gov.justice.digital.hmpps -import org.junit.jupiter.api.Test +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.junit.jupiter.api.* 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.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT -import org.springframework.boot.test.mock.mockito.MockBean +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.get +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.withToken -import uk.gov.justice.digital.hmpps.telemetry.TelemetryService @AutoConfigureMockMvc @SpringBootTest(webEnvironment = RANDOM_PORT) +@TestMethodOrder(MethodOrderer.OrderAnnotation::class) internal class IntegrationTest { @Autowired lateinit var mockMvc: MockMvc - @MockBean - lateinit var telemetryService: TelemetryService + @Autowired + lateinit var ldapTemplate: LdapTemplate @Test - fun `API call retuns a success response`() { + @Order(1) + fun `can add role`() { mockMvc - .perform(get("/example/123").withToken()) + .perform(put("/user/test.user/role").withToken()) .andExpect(status().is2xxSuccessful) + + val role = ldapTemplate.lookupContext(LdapNameBuilder.newInstance("cn=IMSBT001,cn=test.user").build()) + + assertThat(role.dn.toString(), equalTo("cn=IMSBT001,cn=test.user")) + } + + @Test + @Order(2) + fun `can remove role`() { + mockMvc + .perform(delete("/user/test.user/role").withToken()) + .andExpect(status().is2xxSuccessful) + + assertThrows { + ldapTemplate.lookupContext(LdapNameBuilder.newInstance("cn=IMSBT001,cn=test.user").build()) + } } } diff --git a/projects/ims-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/ApiController.kt b/projects/ims-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/ApiController.kt deleted file mode 100644 index 6e4f24c46f..0000000000 --- a/projects/ims-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/ApiController.kt +++ /dev/null @@ -1,17 +0,0 @@ -package uk.gov.justice.digital.hmpps.controller - -import org.springframework.security.access.prepost.PreAuthorize -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.PathVariable -import org.springframework.web.bind.annotation.RestController - -@RestController -class ApiController { - @PreAuthorize("hasRole('EXAMPLE')") - @GetMapping(value = ["/example/{inputId}"]) - fun handle( - @PathVariable("inputId") inputId: String - ) { - // TODO Not yet implemented - } -} diff --git a/projects/ims-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/UserController.kt b/projects/ims-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/UserController.kt new file mode 100644 index 0000000000..77093b816a --- /dev/null +++ b/projects/ims-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/controller/UserController.kt @@ -0,0 +1,29 @@ +package uk.gov.justice.digital.hmpps.controller + +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.RestController +import uk.gov.justice.digital.hmpps.ldap.DeliusRole +import uk.gov.justice.digital.hmpps.ldap.addRole +import uk.gov.justice.digital.hmpps.ldap.removeRole + +@RestController +class UserController(private val ldapTemplate: LdapTemplate) { + @PutMapping(value = ["/user/{username}/role"]) + @PreAuthorize("hasRole('PROBATION_API__PATHFINDER__USER_ROLES__RW')") + fun addRole(@PathVariable username: String) = ldapTemplate.addRole(username, Role.IMSBT001) + + @DeleteMapping(value = ["/user/{username}/role"]) + @PreAuthorize("hasRole('PROBATION_API__PATHFINDER__USER_ROLES__RW')") + fun removeRole(@PathVariable username: String) = ldapTemplate.removeRole(username, Role.IMSBT001) +} + +enum class Role( + override val description: String, + override val mappedRole: String +) : DeliusRole { + IMSBT001("IMS User", "IMSBT001") +} diff --git a/projects/ims-and-delius/src/main/resources/application.yml b/projects/ims-and-delius/src/main/resources/application.yml index 35b0ea52a3..7a2b0c5823 100644 --- a/projects/ims-and-delius/src/main/resources/application.yml +++ b/projects/ims-and-delius/src/main/resources/application.yml @@ -17,9 +17,13 @@ spring: create_tables: false drop_tables: false threads.virtual.enabled: true + ldap: + base: ou=Users,dc=moj,dc=com + base-environment: + java.naming.ldap.derefAliases: never oauth2.roles: - - EXAMPLE + - PROBATION_API__PATHFINDER__USER_ROLES__RW springdoc.default-produces-media-type: application/json @@ -41,6 +45,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} + validation.enabled: false seed.database: true wiremock.enabled: true diff --git a/projects/ims-and-delius/src/main/resources/schema.ldif b/projects/ims-and-delius/src/main/resources/schema.ldif new file mode 100644 index 0000000000..7181f90abe --- /dev/null +++ b/projects/ims-and-delius/src/main/resources/schema.ldif @@ -0,0 +1,24 @@ +dn: ou=Users,dc=moj,dc=com +objectclass: top +objectclass: organizationalUnit +ou: Users + +dn: cn=test.user,ou=Users,dc=moj,dc=com +cn: test.user +objectclass: NDUser +objectclass: inetOrgPerson +objectclass: top +givenName: Test +sn: User +mail: test.user@example.com + +dn: cn=ndRoleCatalogue,ou=Users,dc=moj,dc=com +description: Role Catalogue +objectclass: top +cn: ndRoleCatalogue + +dn: cn=IMSBT001,cn=ndRoleCatalogue,ou=Users,dc=moj,dc=com +description: Navigate to Intelligence Management System +objectClass: NDRole +objectClass: top +cn: IMSBT001 \ No newline at end of file