Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PI-2479: Return not found when LDAP user not found #4241

Merged
merged 1 commit into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package uk.gov.justice.digital.hmpps.ldap

import io.opentelemetry.instrumentation.annotations.SpanAttribute
import io.opentelemetry.instrumentation.annotations.WithSpan
import org.springframework.ldap.NameNotFoundException
import org.springframework.ldap.core.AttributesMapper
import org.springframework.ldap.core.LdapTemplate
import org.springframework.ldap.query.LdapQuery
Expand All @@ -25,26 +26,32 @@ inline fun <reified T> LdapTemplate.findByUsername(@SpanAttribute username: Stri
fun LdapTemplate.findEmailByUsername(@SpanAttribute username: String) = findAttributeByUsername(username, "mail")

@WithSpan
fun LdapTemplate.findAttributeByUsername(@SpanAttribute username: String, @SpanAttribute attribute: String) = search(
query()
fun LdapTemplate.findAttributeByUsername(@SpanAttribute username: String, @SpanAttribute attribute: String) = try {
search(query()
.attributes(attribute)
.searchScope(SearchScope.ONELEVEL)
.where("objectclass").`is`("inetOrgPerson")
.and("objectclass").`is`("top")
.and("cn").`is`(username),
AttributesMapper { it[attribute]?.get()?.toString() }
).singleOrNull()
AttributesMapper { it[attribute]?.get()?.toString() }
).singleOrNull()
} catch (_: NameNotFoundException) {
throw NotFoundException("NDeliusUser of $username not found")
}

@WithSpan
fun LdapTemplate.getRoles(@SpanAttribute username: String) = search(
query()
fun LdapTemplate.getRoles(@SpanAttribute username: String) = try {
search(query()
.attributes("cn")
.base(LdapNameBuilder.newInstance().add("cn", username).build())
.searchScope(SearchScope.ONELEVEL)
.where("objectclass").`is`("NDRole")
.or("objectclass").`is`("NDRoleAssociation"),
AttributesMapper { it["cn"]?.get()?.toString() }
).filterNotNull()
AttributesMapper { it["cn"]?.get()?.toString() }
).filterNotNull()
} catch (_: NameNotFoundException) {
throw NotFoundException("NDeliusUser of $username not found")
}

@WithSpan
fun LdapTemplate.addRole(@SpanAttribute username: String, @SpanAttribute role: DeliusRole) {
Expand All @@ -56,12 +63,20 @@ fun LdapTemplate.addRole(@SpanAttribute username: String, @SpanAttribute role: D
put(listOf("NDRoleAssociation", "alias", "top").asAttribute("objectclass"))
}
val userRole = role.context(username)
rebind(userRole, null, attributes)
try {
rebind(userRole, null, attributes)
} catch (_: NameNotFoundException) {
throw NotFoundException("NDeliusUser of $username not found")
}
}

@WithSpan
fun LdapTemplate.removeRole(@SpanAttribute username: String, @SpanAttribute role: DeliusRole) =
unbind(role.context(username))
try {
unbind(role.context(username))
} catch (_: NameNotFoundException) {
throw NotFoundException("NDeliusUser of $username not found")
}

private fun DeliusRole.context(username: String? = null) =
LdapNameBuilder.newInstance()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.*
import org.springframework.ldap.NameNotFoundException
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.Name
import javax.naming.directory.Attributes
import javax.naming.ldap.LdapName

Expand Down Expand Up @@ -116,4 +118,59 @@ class LdapTemplateExtensionsTest {
assertThat(it.toString(), equalTo("cn=ROLE1,cn=john-smith"))
})
}

@Test
fun `unknown username throws NotFoundException when getting roles`() {

whenever(ldapTemplate.search(any(), any<AttributesMapper<String?>>()))
.thenThrow(NameNotFoundException("No Such Object"))

assertThrows<NotFoundException> { ldapTemplate.getRoles("test") }
}

@Test
fun `unknown username throws NotFoundException finding by username`() {

whenever(ldapTemplate.search(any(), any<AttributesMapper<String?>>()))
.thenThrow(NameNotFoundException("No Such Object"))

assertThrows<NotFoundException> { ldapTemplate.findEmailByUsername("test") }
}

@Test
fun `unknown username throws NotFoundException when adding roles`() {
whenever(ldapTemplate.lookupContext(any<LdapName>()))
.thenReturn(dirContextOperations)
whenever(dirContextOperations.nameInNamespace)
.thenReturn("cn=ROLE1,cn=ndRoleCatalogue,ou=Users,dc=moj,dc=com")

whenever(ldapTemplate.rebind(any<Name>(), anyOrNull(), any<Attributes>())).thenThrow(
NameNotFoundException("No Such Object")
)
whenever(ldapTemplate.unbind(any<Name>())).thenThrow(
NameNotFoundException("No Such Object")
)

assertThrows<NotFoundException> {
ldapTemplate.addRole(
"test",
object : DeliusRole {
override val description = "Role One Description"
override val mappedRole = "MAPPED_ROLE_ONE"
override val name = "ROLE1"
}
)
}

assertThrows<NotFoundException> {
ldapTemplate.removeRole(
"test",
object : DeliusRole {
override val description = "Role One Description"
override val mappedRole = "MAPPED_ROLE_ONE"
override val name = "ROLE1"
}
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,16 @@ internal class UserIntegrationTest {
mockMvc.perform(delete("/users/test.user/roles/LHDCBT003").withToken())
.andExpect(status().isOk)
}

@Test
fun `non existent user returns not found`() {
mockMvc.perform(put("/users/nonexistent.user/roles/LHDCBT003").withToken())
.andExpect(status().isNotFound)
mockMvc
.perform(delete("/users/nonexistent.user/roles/LHDCBT003").withToken())
.andExpect(status().isNotFound)
mockMvc
.perform(get("/users/nonexistent.user/details").withToken())
.andExpect(status().isNotFound)
}
}
Loading