Skip to content

Commit

Permalink
PI-1508 HDC Licences endpoints (#2642)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcus-bcl authored Nov 22, 2023
1 parent e742551 commit 247be97
Show file tree
Hide file tree
Showing 43 changed files with 1,058 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,31 @@ inline fun <reified T> LdapTemplate.findByUsername(@SpanAttribute username: Stri
find(query().byUsername(username), T::class.java).singleOrNull()

@WithSpan
fun LdapTemplate.findEmailByUsername(@SpanAttribute username: String) = search(
fun LdapTemplate.findEmailByUsername(@SpanAttribute username: String) = findAttributeByUsername(username, "mail")

@WithSpan
fun LdapTemplate.findAttributeByUsername(@SpanAttribute username: String, @SpanAttribute attribute: String) = search(
query()
.attributes("mail")
.attributes(attribute)
.base(ldapBase)
.searchScope(SearchScope.ONELEVEL)
.where("objectclass").`is`("inetOrgPerson")
.and("objectclass").`is`("top")
.and("cn").`is`(username),
AttributesMapper { it["mail"]?.get()?.toString() }
AttributesMapper { it[attribute]?.get()?.toString() }
).singleOrNull()

@WithSpan
fun LdapTemplate.getRoles(@SpanAttribute username: String) = search(
query()
.attributes("cn")
.base(LdapNameBuilder.newInstance(ldapBase).add("cn", username).build())
.searchScope(SearchScope.ONELEVEL)
.where("objectclass").`is`("NDRole")
.or("objectclass").`is`("NDRoleAssociation"),
AttributesMapper { it["cn"]?.get()?.toString() }
).filterNotNull()

@WithSpan
fun LdapTemplate.addRole(@SpanAttribute username: String, @SpanAttribute role: DeliusRole) {
val roleContext = lookupContext(role.context())
Expand Down
3 changes: 3 additions & 0 deletions projects/hdc-licences-and-delius/.trivyignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Suppressed as we do not process any untrusted YML content
# Note: this will be resolved in Spring Boot 3.2: https://github.com/spring-projects/spring-boot/issues/35982
CVE-2022-1471 exp:2023-12-01
2 changes: 2 additions & 0 deletions projects/hdc-licences-and-delius/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,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")
Expand All @@ -19,6 +20,7 @@ dependencies {
implementation(libs.springdoc)

dev(project(":libs:dev-tools"))
dev("com.unboundid:unboundid-ldapsdk")
dev("com.h2database:h2")
dev("org.testcontainers:oracle-xe")

Expand Down
2 changes: 0 additions & 2 deletions projects/hdc-licences-and-delius/deploy/values-dev.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
enabled: false # TODO set this to true when you're ready to deploy your service

generic-service:
ingress:
host: hdc-licences-and-delius-dev.hmpps.service.justice.gov.uk
Expand Down
2 changes: 0 additions & 2 deletions projects/hdc-licences-and-delius/deploy/values-preprod.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
enabled: false # TODO set this to true when you're ready to deploy your service

generic-service:
ingress:
host: hdc-licences-and-delius-preprod.hmpps.service.justice.gov.uk
Expand Down
2 changes: 0 additions & 2 deletions projects/hdc-licences-and-delius/deploy/values-prod.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
enabled: false # TODO set this to true when you're ready to deploy your service

generic-service:
ingress:
host: hdc-licences-and-delius.hmpps.service.justice.gov.uk
Expand Down
3 changes: 3 additions & 0 deletions projects/hdc-licences-and-delius/deploy/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,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
hdc-licences-and-delius-database:
SPRING_DATASOURCE_USERNAME: DB_USERNAME
SPRING_DATASOURCE_PASSWORD: DB_PASSWORD
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,58 @@
package uk.gov.justice.digital.hmpps.data

import jakarta.annotation.PostConstruct
import jakarta.persistence.EntityManager
import jakarta.transaction.Transactional
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.event.ApplicationReadyEvent
import org.springframework.context.ApplicationListener
import org.springframework.stereotype.Component
import uk.gov.justice.digital.hmpps.data.generator.IdGenerator
import uk.gov.justice.digital.hmpps.data.generator.UserGenerator
import uk.gov.justice.digital.hmpps.entity.Borough
import uk.gov.justice.digital.hmpps.entity.CommunityManagerEntity
import uk.gov.justice.digital.hmpps.entity.District
import uk.gov.justice.digital.hmpps.entity.Person
import uk.gov.justice.digital.hmpps.entity.ProbationArea
import uk.gov.justice.digital.hmpps.entity.StaffEntity
import uk.gov.justice.digital.hmpps.entity.Team
import uk.gov.justice.digital.hmpps.entity.User
import uk.gov.justice.digital.hmpps.set
import uk.gov.justice.digital.hmpps.user.AuditUserRepository

@Component
@ConditionalOnProperty("seed.database")
class DataLoader(
private val auditUserRepository: AuditUserRepository
private val auditUserRepository: AuditUserRepository,
private val entityManager: EntityManager
) : ApplicationListener<ApplicationReadyEvent> {

@PostConstruct
fun saveAuditUser() {
auditUserRepository.save(UserGenerator.AUDIT_USER)
}

@Transactional
override fun onApplicationEvent(are: ApplicationReadyEvent) {
// Perform dev/test database setup here, using JPA repositories and generator classes...
val probationArea = ProbationArea(id = id(), code = "TST", description = "Test")
val borough = Borough(id = id(), code = "PDU", description = "Probation Delivery Unit", probationArea = probationArea)
.also { probationArea.set(ProbationArea::boroughs, setOf(it)) }
val district = District(id = id(), code = "LAU", description = "Local Admin Unit", borough = borough)
.also { borough.set(Borough::districts, setOf(it)) }
val team1 = Team(id = id(), code = "TEAM01", description = "Team 1", district = district, probationArea = probationArea)
val team2 = Team(id = id(), code = "TEAM02", description = "Team 2", district = district, probationArea = probationArea)
val staff = StaffEntity(id = id(), code = "STAFF01", forename = "Test", surname = "Staff", teams = listOf(team1, team2))
val user = User(id = id(), username = "test.user", staff = staff)
.also { staff.set(StaffEntity::user, it) }
val person = Person(id = id(), nomsNumber = "PERSON1")
val previousManager = CommunityManagerEntity(id = id(), person = person, staff = staff, team = team1, active = false)
val currentManager = CommunityManagerEntity(id = id(), person = person, staff = staff, team = team2, active = true)
.also { staff.set(StaffEntity::communityManagers, setOf(it)) }
.also { person.set(Person::communityManagers, listOf(it)) }

listOf(probationArea, borough, district, team1, team2, staff, user, person, previousManager, currentManager)
.forEach(entityManager::persist)
}

private fun id() = IdGenerator.getAndIncrement()
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo3hw1/oChbttEOxEH4NUDrH+Y
n2x0DavAmDjMbhcSiQ6+/t8Nz/N03BauWzFOGBtftnQrHfnF+O7RAKj8zMjcbIq4
QrYeXEpnaFCGEwTtOBpxvSEWPrLEpr1gCarBQZDp67ag+SYqrDgkn2Vme/dMvMUQ
xUO3DT6jg9921J6TlwIDAQAB
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMwjPPJWNVkkbz1rvp5gfIgikJ
tiar18XcBScET2wOs7nAviD0nBzLByMGfexX0LQz3J9+S0Ut/bSqPyrgPe9Wc5s9
6JwH6biAYzuLOVhLXBpmwGP6FpZIoSA+VkwApLN9j8qPBvAYrIftpTWsEq090VUE
WHvs4bqvS01yN7IPTwIDAQAB
-----END PUBLIC KEY-----
49 changes: 49 additions & 0 deletions projects/hdc-licences-and-delius/src/dev/resources/schema.ldif
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
dn: dc=moj,dc=com
dc: moj
objectclass: top
objectclass: domain

dn: ou=Users,dc=moj,dc=com
ou: Users
objectclass: top
objectclass: organizationalUnit

dn: cn=ndRoleCatalogue,ou=Users,dc=moj,dc=com
cn: ndRoleCatalogue
objectclass: top
objectclass: javaContainer

dn: cn=LHDCBT002,cn=ndRoleCatalogue,ou=Users,dc=moj,dc=com
cn: LHDCBT002
description: Digital Licence Create
objectclass: top
objectClass: NDRole

dn: cn=LHDCBT003,cn=ndRoleCatalogue,ou=Users,dc=moj,dc=com
cn: LHDCBT003
description: Digital Licence Vary
objectclass: top
objectClass: NDRole

dn: cn=test.user,ou=Users,dc=moj,dc=com
cn: test.user
objectclass: NDUser
objectclass: inetOrgPerson
objectclass: top
givenName: Test
sn: User
mail: [email protected]

dn: cn=LHDCBT002,cn=test.user,ou=Users,dc=moj,dc=com
cn: LHDCBT002
objectclass: top
objectClass: NDRoleAssociation

dn: cn=inactive.user,ou=Users,dc=moj,dc=com
cn: inactive.user
objectclass: NDUser
objectclass: inetOrgPerson
objectclass: top
givenName: Inactive
sn: User
endDate: 20000101
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJwcm9iYXRpb24taW50ZWdyYXRpb24tZGV2IiwiZ3JhbnRfdHlwZSI6ImNsaWVudF9jcmVkZW50aWFscyIsInVzZXJfbmFtZSI6InByb2JhdGlvbi1pbnRlZ3JhdGlvbi1kZXYiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXV0aF9zb3VyY2UiOiJub25lIiwiaXNzIjoiaHR0cHM6Ly9zaWduLWluLWRldi5obXBwcy5zZXJ2aWNlLmp1c3RpY2UuZ292LnVrL2F1dGgvaXNzdWVyIiwiZXhwIjo5OTk5OTk5OTk5LCJhdXRob3JpdGllcyI6WyJST0xFX0VYQU1QTEUiLCJST0xFX1dPUktMT0FEX1JFQUQiLCJST0xFX0FMTE9DQVRJT05fQ09OVEVYVCJdLCJqdGkiOiIyNUR1Um4xLWh5SFpld0xjZEpKeHdWTDAzS1UiLCJjbGllbnRfaWQiOiJwcm9iYXRpb24taW50ZWdyYXRpb24tZGV2IiwiaWF0IjoxNjYzNzU3MzExfQ.5FTCUjA7QZMPxO_EMzkGNSM-IkPk2hfPXyzuNiAa7uuqYva_yCducrC5FdetAiC1W6XpUB7wfoMNDmbW2xepj5oRhcxDx18r92aLPYnKkxaA68hLQF90euMtTzfBzOPg-rKDTNIJKrUC-YoQlFKuCauw0Z5cw1XT6R9GIfi5Yx4",
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJwcm9iYXRpb24taW50ZWdyYXRpb24tZGV2IiwiZ3JhbnRfdHlwZSI6ImNsaWVudF9jcmVkZW50aWFscyIsInVzZXJfbmFtZSI6InByb2JhdGlvbi1pbnRlZ3JhdGlvbi1kZXYiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXV0aF9zb3VyY2UiOiJub25lIiwiaXNzIjoiaHR0cHM6Ly9zaWduLWluLWRldi5obXBwcy5zZXJ2aWNlLmp1c3RpY2UuZ292LnVrL2F1dGgvaXNzdWVyIiwiZXhwIjo5OTk5OTk5OTk5LCJhdXRob3JpdGllcyI6WyJST0xFX1BST0JBVElPTl9BUElfX0hEQ19fU1RBRkYiLCJST0xFX1BST0JBVElPTl9BUElfX0hEQ19fVVNFUl9ST0xFUyJdLCJqdGkiOiIyNUR1Um4xLWh5SFpld0xjZEpKeHdWTDAzS1UiLCJjbGllbnRfaWQiOiJwcm9iYXRpb24taW50ZWdyYXRpb24tZGV2IiwiaWF0IjoxNjYzNzU3MzExfQ.l90PSVBcVIRA7sgNkVG4UZElkSm1eUqc6N8XId5sIz7mfmcY3IqV3ksErknw0oSRvPb3ZbzCZ4Pc7KoWdfrXouYHgzrYQZfIYCJp7PqyHbfrdr_B46X2Q_QtzhN2Ec7ku6A4NNSj4yp6tW8i4c_xuaw3NwcpgT-s_xZ39nT4Mg4",
"token_type": "bearer",
"expires_in": 9999999999,
"scope": "read write",
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package uk.gov.justice.digital.hmpps

import com.github.tomakehurst.wiremock.WireMockServer
import org.hamcrest.Matchers.equalTo
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.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import uk.gov.justice.digital.hmpps.security.withOAuth2Token

@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = RANDOM_PORT)
internal class ProviderIntegrationTest {
@Autowired
lateinit var mockMvc: MockMvc

@Autowired
lateinit var wireMockServer: WireMockServer

@Test
fun `providers are returned successfully`() {
mockMvc
.perform(get("/providers").withOAuth2Token(wireMockServer))
.andExpect(status().isOk)
.andExpect(jsonPath("length()", equalTo(1)))
.andExpect(jsonPath("[0].code", equalTo("TST")))
.andExpect(jsonPath("[0].description", equalTo("Test")))
}

@Test
fun `single provider is returned successfully`() {
mockMvc
.perform(get("/providers/TST").withOAuth2Token(wireMockServer))
.andExpect(status().isOk)
.andExpect(jsonPath("code", equalTo("TST")))
.andExpect(jsonPath("description", equalTo("Test")))
.andExpect(jsonPath("localAdminUnits.length()", equalTo(1)))
.andExpect(jsonPath("localAdminUnits[0].code", equalTo("LAU")))
.andExpect(jsonPath("localAdminUnits[0].description", equalTo("Local Admin Unit")))
}

@Test
fun `non-existent provider returns 404`() {
mockMvc
.perform(get("/providers/DOESNOTEXIST").withOAuth2Token(wireMockServer))
.andExpect(status().isNotFound)
}

@Test
fun `local admin unit is returned successfully`() {
mockMvc
.perform(get("/providers/TST/localAdminUnits/LAU").withOAuth2Token(wireMockServer))
.andExpect(status().isOk)
.andExpect(jsonPath("code", equalTo("LAU")))
.andExpect(jsonPath("description", equalTo("Local Admin Unit")))
.andExpect(jsonPath("teams.length()", equalTo(2)))
.andExpect(jsonPath("teams[0].code", equalTo("TEAM01")))
.andExpect(jsonPath("teams[0].description", equalTo("Team 1")))
.andExpect(jsonPath("teams[1].code", equalTo("TEAM02")))
.andExpect(jsonPath("teams[1].description", equalTo("Team 2")))
}

@Test
fun `non-existent local admin unit returns 404`() {
mockMvc
.perform(get("/providers/TST/localAdminUnits/DOESNOTEXIST").withOAuth2Token(wireMockServer))
.andExpect(status().isNotFound)
}
}
Loading

0 comments on commit 247be97

Please sign in to comment.