Skip to content

Commit

Permalink
updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Foxcapades committed Feb 16, 2023
1 parent bdd0961 commit 1ccaf15
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 0 deletions.
18 changes: 18 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,22 @@ plugins {

repositories {
mavenCentral()
}

kotlin {
jvmToolchain(18)
}

dependencies {
implementation("org.slf4j:slf4j-api:1.7.36")
implementation("com.unboundid:unboundid-ldapsdk:6.0.7")

testImplementation(kotlin("test"))
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0")
testImplementation("org.mockito:mockito-core:4.8.0")
}

tasks.test {
useJUnitPlatform()
}
3 changes: 3 additions & 0 deletions readme.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
= LDAP Utils

Utilities for working with LDAP from VEuPathDB containerized services.
86 changes: 86 additions & 0 deletions src/main/kotlin/org/veupathdb/lib/ldap/LDAP.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package org.veupathdb.lib.ldap

import com.unboundid.ldap.sdk.*
import org.slf4j.LoggerFactory

class LDAP(private val config: LDAPConfig) {
private val log = LoggerFactory.getLogger(javaClass)

private var ldapConnection: LDAPConnection? = null

init {
if (config.hosts.isEmpty())
throw IllegalArgumentException("Passed the $javaClass constructor a config with 0 hosts entries")
}

fun requireSingularOracleNetDesc(commonName: String): OracleNetDesc {
log.trace("requireSingularOracleNetDesc(commonName={})", commonName)

val tmp = lookupOracleNetDesc(commonName)

if (tmp.isEmpty())
throw IllegalStateException("no OracleNetDescs found for common name $commonName")
if (tmp.size > 1)
throw IllegalStateException("multiple OracleNetDescs found for common name $commonName")

return tmp[0]
}

fun lookupOracleNetDesc(commonName: String): List<OracleNetDesc> {
log.trace("lookupOracleNetDesc(commonName={})", commonName)

return getConnection()
.search(SearchRequest(
config.oracleBaseDN,
SearchScope.SUB,
Filter.createANDFilter(
Filter.create("cn=$commonName"),
Filter.create("objectClass=orclNetService")
),
"orclNetDescString"
))
.searchEntries
.map { OracleNetDesc(it.getAttribute("orclNetDescString").value!!) }
}

private fun getConnection(): LDAPConnection {
log.trace("getConnection()")

// Synchronized because this thing is gonna be called from who knows where.
synchronized(this) {

// If we've already got an LDAP connection
if (ldapConnection != null) {

// If the LDAP connection we've already got is still connected
if (ldapConnection!!.isConnected)
// then return it
return ldapConnection!!
// else, the LDAP connection we've already got is _not_ still connected
else
// then disregard it
ldapConnection = null
}

log.debug("Attempting to establish a connection to a configured LDAP server")
for (host in config.hosts) {
log.trace("Trying to connect to {}:{}", host.host, host.port)

try {
ldapConnection =LDAPConnection(host.host, host.port.toInt())
.also { log.debug("Connected to {}:{}", host.host, host.port) }
break
} catch (e: Throwable) {
log.debug("Failed to connect to {}:{}", host.host, host.port)
}
}

if (ldapConnection == null) {
log.error("Failed to establish a connection to any configured LDAP server.")
throw RuntimeException("Failed to establish a connection to any configured LDAP server.")
}

return ldapConnection!!
}
}
}
3 changes: 3 additions & 0 deletions src/main/kotlin/org/veupathdb/lib/ldap/LDAPConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.veupathdb.lib.ldap

data class LDAPConfig(val hosts: Collection<LDAPHost>, val oracleBaseDN: String)
15 changes: 15 additions & 0 deletions src/main/kotlin/org/veupathdb/lib/ldap/LDAPHost.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.veupathdb.lib.ldap

data class LDAPHost(val host: String, val port: UShort) {
companion object {
@JvmStatic
fun ofString(str: String): LDAPHost {
val colonIndex = str.indexOf(':')

if (colonIndex < 1)
throw IllegalArgumentException("input string $str did not resemble a valid \"host:port\" string")

return LDAPHost(str.substring(0, colonIndex), str.substring(colonIndex + 1).toUShort())
}
}
}
58 changes: 58 additions & 0 deletions src/main/kotlin/org/veupathdb/lib/ldap/OracleNetDesc.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.veupathdb.lib.ldap

import java.math.BigInteger

private const val HOST_PREFIX = "(HOST="
private const val PORT_PREFIX = "(PORT="
private const val SERVICE_NAME_PREFIX = "(SERVICE_NAME="
private const val VALUE_SUFFIX = ')'

data class OracleNetDesc(
val host: String,
val port: UShort,
val serviceName: String,
) {
constructor(string: String) : this(
string.requireHostValue(),
string.requirePortValue(),
string.requireServiceNameValue(),
)
}

private fun String.requireHostValue() = requireValue(HOST_PREFIX, "HOST")

private fun String.requirePortValue(): UShort {
val bi = try {
BigInteger(requireValue(PORT_PREFIX, "PORT"))
} catch (e: Throwable) {
throw IllegalArgumentException("given orclNetDescString contained an invalid PORT value")
}

if (bi > BigInteger.valueOf(65535))
throw IllegalArgumentException("given orclNetDescString contained a PORT value that was too large to be a valid port")
if (bi < BigInteger.ZERO)
throw IllegalArgumentException("given orclNetDescString contained a PORT value that was less than zero")

return bi.toInt().toUShort()
}

private fun String.requireServiceNameValue(): String = requireValue(SERVICE_NAME_PREFIX, "SERVICE_NAME")

private fun String.requireValue(prefix: String, name: String): String {
val start = indexOf(prefix)

if (start < 0)
throw IllegalArgumentException("given orclNetDescString did not contain a $name value")

val end = indexOf(VALUE_SUFFIX, start)

if (end < 0)
throw IllegalArgumentException("malformed orclNetDescString value")

val out = substring(start + prefix.length, end)

if (out.isEmpty())
throw IllegalArgumentException("given orclNetDescString contained an empty $name value")

return out
}
18 changes: 18 additions & 0 deletions src/test/kotlin/org/veupathdb/lib/ldap/LDAPHostTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.veupathdb.lib.ldap

import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test

@DisplayName("LDAPHost")
class LDAPHostTest {

@Test
@DisplayName("ofString(String)")
fun t1() {
val tgt = LDAPHost.ofString("something:1234")

assertEquals("something", tgt.host)
assertEquals(1234.toUShort(), tgt.port)
}
}

0 comments on commit 1ccaf15

Please sign in to comment.