LDAP authentication by using bind account

This commit is contained in:
Tomofumi Tanaka
2013-08-21 16:37:10 +09:00
committed by Tomofumi Tanaka
parent b9aa6a234b
commit bfc1d1d6b0
4 changed files with 114 additions and 31 deletions

View File

@@ -27,7 +27,9 @@ trait SystemSettingsControllerBase extends ControllerBase with FlashMapSupport {
"ldap" -> optionalIfNotChecked("ldapAuthentication", mapping( "ldap" -> optionalIfNotChecked("ldapAuthentication", mapping(
"host" -> trim(label("LDAP host", text(required))), "host" -> trim(label("LDAP host", text(required))),
"port" -> trim(label("LDAP port", optional(number()))), "port" -> trim(label("LDAP port", optional(number()))),
"baseDN" -> trim(label("BaseDN", text(required))), "bindDN" -> trim(label("Bind DN", text(required))),
"bindPassword" -> trim(label("Bind Password", text(required))),
"baseDN" -> trim(label("Base DN", text(required))),
"userNameAttribute" -> trim(label("User name attribute", text(required))), "userNameAttribute" -> trim(label("User name attribute", text(required))),
"mailAttribute" -> trim(label("Mail address attribute", text(required))) "mailAttribute" -> trim(label("Mail address attribute", text(required)))
)(Ldap.apply)) )(Ldap.apply))

View File

@@ -24,6 +24,8 @@ trait SystemSettingsService {
settings.ldap.map { ldap => settings.ldap.map { ldap =>
props.setProperty(LdapHost, ldap.host) props.setProperty(LdapHost, ldap.host)
ldap.port.foreach(x => props.setProperty(LdapPort, x.toString)) ldap.port.foreach(x => props.setProperty(LdapPort, x.toString))
props.setProperty(LdapBindDN, ldap.bindDN)
props.setProperty(LdapBindPassword, ldap.bindPassword)
props.setProperty(LdapBaseDN, ldap.baseDN) props.setProperty(LdapBaseDN, ldap.baseDN)
props.setProperty(LdapUserNameAttribute, ldap.userNameAttribute) props.setProperty(LdapUserNameAttribute, ldap.userNameAttribute)
props.setProperty(LdapMailAddressAttribute, ldap.mailAttribute) props.setProperty(LdapMailAddressAttribute, ldap.mailAttribute)
@@ -57,6 +59,8 @@ trait SystemSettingsService {
Some(Ldap( Some(Ldap(
getValue(props, LdapHost, ""), getValue(props, LdapHost, ""),
getOptionValue(props, LdapPort, Some(DefaultLdapPort)), getOptionValue(props, LdapPort, Some(DefaultLdapPort)),
getValue(props, LdapBindDN, ""),
getValue(props, LdapBindPassword, ""),
getValue(props, LdapBaseDN, ""), getValue(props, LdapBaseDN, ""),
getValue(props, LdapUserNameAttribute, ""), getValue(props, LdapUserNameAttribute, ""),
getValue(props, LdapMailAddressAttribute, ""))) getValue(props, LdapMailAddressAttribute, "")))
@@ -82,6 +86,8 @@ object SystemSettingsService {
case class Ldap( case class Ldap(
host: String, host: String,
port: Option[Int], port: Option[Int],
bindDN: String,
bindPassword: String,
baseDN: String, baseDN: String,
userNameAttribute: String, userNameAttribute: String,
mailAttribute: String) mailAttribute: String)
@@ -106,6 +112,8 @@ object SystemSettingsService {
private val LdapAuthentication = "ldap_authentication" private val LdapAuthentication = "ldap_authentication"
private val LdapHost = "ldap.host" private val LdapHost = "ldap.host"
private val LdapPort = "ldap.port" private val LdapPort = "ldap.port"
private val LdapBindDN = "ldap.bindDN"
private val LdapBindPassword = "ldap.bind_password"
private val LdapBaseDN = "ldap.baseDN" private val LdapBaseDN = "ldap.baseDN"
private val LdapUserNameAttribute = "ldap.username_attribute" private val LdapUserNameAttribute = "ldap.username_attribute"
private val LdapMailAddressAttribute = "ldap.mail_attribute" private val LdapMailAddressAttribute = "ldap.mail_attribute"

View File

@@ -2,49 +2,107 @@ package util
import service.SystemSettingsService.Ldap import service.SystemSettingsService.Ldap
import service.SystemSettingsService import service.SystemSettingsService
import com.novell.ldap.LDAPConnection import com.novell.ldap.{LDAPReferralException, LDAPEntry, LDAPConnection}
/** /**
* Utility for LDAP authentication. * Utility for LDAP authentication.
*/ */
object LDAPUtil extends App { object LDAPUtil {
private val LDAP_VERSION: Int = 3
/** /**
* Try authentication by LDAP using given configuration. * Try authentication by LDAP using given configuration.
* Returns Right(mailAddress) if authentication is successful, otherwise Left(errorMessage). * Returns Right(mailAddress) if authentication is successful, otherwise Left(errorMessage).
*/ */
def authenticate(ldapSettings: Ldap, userName: String, password: String): Either[String, String] = { def authenticate(ldapSettings: Ldap, userName: String, password: String): Either[String, String] = {
var conn: LDAPConnection = null bind(
ldapSettings.host,
ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort),
ldapSettings.bindDN,
ldapSettings.bindPassword
) match {
case Some(conn) => {
withConnection(conn) { conn =>
findUser(conn, userName, ldapSettings.baseDN, ldapSettings.userNameAttribute) match {
case Some(userDN) => userAuthentication(ldapSettings, userDN, password)
case None => Left("User does not exist")
}
}
}
case None => Left("System LDAP authentication failed.")
}
}
private def userAuthentication(ldapSettings: Ldap, userDN: String, password: String): Either[String, String] = {
bind(
ldapSettings.host,
ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort),
userDN,
password
) match {
case Some(conn) => {
withConnection(conn) { conn =>
findMailAddress(conn, userDN, ldapSettings.mailAttribute) match {
case Some(mailAddress) => Right(mailAddress)
case None => Left("Can't find mail address.")
}
}
}
case None => Left("User LDAP Authentication Failed.")
}
}
private def bind(host: String, port: Int, dn: String, password: String): Option[LDAPConnection] = {
val conn: LDAPConnection = new LDAPConnection
try { try {
conn = new LDAPConnection() conn.connect(host, port)
conn.connect(ldapSettings.host, ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort)) conn.bind(LDAP_VERSION, dn, password.getBytes)
val userDN = ldapSettings.userNameAttribute + "=" + userName + ",ou=Users," + ldapSettings.baseDN Some(conn)
conn.bind(3, userDN, password.getBytes)
if(conn.isBound){
val results = conn.search(userDN, LDAPConnection.SCOPE_BASE, "", Array[String](ldapSettings.mailAttribute), false)
var mailAddress: String = null
while(results.hasMore){
mailAddress = results.next.getAttribute(ldapSettings.mailAttribute).getStringValue
}
if(mailAddress != null){
Right(mailAddress)
} else {
Left("Can't find mail address.")
}
} else {
Left("Authentication failed.")
}
} catch { } catch {
case ex: Exception => Left(ex.getMessage) case e: Exception => {
if (conn.isConnected) conn.disconnect()
None
}
}
}
private def withConnection[T](conn: LDAPConnection)(f: LDAPConnection => T): T = {
try {
f(conn)
} finally { } finally {
if(conn != null){
conn.disconnect() conn.disconnect()
} }
} }
private def findUser(conn: LDAPConnection, userName: String, baseDN: String, userNameAttribute: String): Option[String] = {
val results = conn.search(baseDN, LDAPConnection.SCOPE_SUB, userNameAttribute + "=" + userName, null, false)
while (results.hasMore) {
var entry: LDAPEntry = null
try {
entry = results.next
} catch {
case lre: LDAPReferralException => // NOTE(tanacasino): Referral follow is off. so ignores it.(for AD)
}
if (entry != null) {
return Some(entry.getDN)
}
}
None
} }
// val ldapSettings = Ldap("192.168.159.128", 389, "dc=unix-power,dc=net", "uid", "mail") private def findMailAddress(conn: LDAPConnection, userDN: String, mailAttribute: String): Option[String] = {
// val attributes = Array[String](mailAttribute)
// println(authenticate(ldapSettings, "tanaka", "password")) val results = conn.search(userDN, LDAPConnection.SCOPE_BASE, null, attributes, false)
if (results.hasMore) {
val attr = results.next.getAttribute(mailAttribute)
if (attr != null) {
Some(attr.getStringValue)
} else {
None
}
} else {
None
}
}
} }

View File

@@ -60,7 +60,22 @@
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="ldapBaseDN">BaseDN</label> <label class="control-label" for="ldapBindDN">Bind DN</label>
<div class="controls">
<input type="text" id="ldapBindDN" name="ldap.bindDN" value="@settings.ldap.map(_.bindDN)"/>
<span id="error-ldap_bindDN" class="error"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="ldapBindPassword">Bind Password</label>
<div class="controls">
<input type="password" id="ldapBindPassword" name="ldap.bindPassword" value="@settings.ldap.map(_.bindPassword)"/>
<span id="error-ldap_bindPassword" class="error"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="ldapBaseDN">Base DN</label>
<div class="controls"> <div class="controls">
<input type="text" id="ldapBaseDN" name="ldap.baseDN" value="@settings.ldap.map(_.baseDN)"/> <input type="text" id="ldapBaseDN" name="ldap.baseDN" value="@settings.ldap.map(_.baseDN)"/>
<span id="error-ldap_baseDN" class="error"></span> <span id="error-ldap_baseDN" class="error"></span>