(refs #245)Add full name attribute for LDAP authentication.

This commit is contained in:
takezoe
2014-01-25 05:07:32 +09:00
parent 94bd1c6a93
commit 5a1f541e13
5 changed files with 65 additions and 48 deletions

View File

@@ -33,6 +33,7 @@ trait SystemSettingsControllerBase extends ControllerBase with FlashMapSupport {
"bindPassword" -> trim(label("Bind Password", optional(text()))), "bindPassword" -> trim(label("Bind Password", optional(text()))),
"baseDN" -> trim(label("Base DN", 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))),
"fullNameAttribute" -> trim(label("Full name attribute", optional(text()))),
"mailAttribute" -> trim(label("Mail address attribute", text(required))), "mailAttribute" -> trim(label("Mail address attribute", text(required))),
"tls" -> trim(label("Enable TLS", optional(boolean()))), "tls" -> trim(label("Enable TLS", optional(boolean()))),
"keystore" -> trim(label("Keystore", optional(text()))) "keystore" -> trim(label("Keystore", optional(text())))

View File

@@ -36,11 +36,11 @@ trait AccountService {
*/ */
private def ldapAuthentication(settings: SystemSettings, userName: String, password: String) = { private def ldapAuthentication(settings: SystemSettings, userName: String, password: String) = {
LDAPUtil.authenticate(settings.ldap.get, userName, password) match { LDAPUtil.authenticate(settings.ldap.get, userName, password) match {
case Right(mailAddress) => { case Right(ldapUserInfo) => {
// Create or update account by LDAP information // Create or update account by LDAP information
getAccountByUserName(userName) match { getAccountByUserName(userName) match {
case Some(x) => updateAccount(x.copy(mailAddress = mailAddress)) case Some(x) => updateAccount(x.copy(mailAddress = ldapUserInfo.mailAddress, fullName = ldapUserInfo.fullName))
case None => createAccount(userName, "", userName, mailAddress, false, None) case None => createAccount(userName, "", ldapUserInfo.fullName, ldapUserInfo.mailAddress, false, None)
} }
getAccountByUserName(userName) getAccountByUserName(userName)
} }

View File

@@ -31,6 +31,7 @@ trait SystemSettingsService {
ldap.bindPassword.foreach(x => props.setProperty(LdapBindPassword, x)) ldap.bindPassword.foreach(x => props.setProperty(LdapBindPassword, x))
props.setProperty(LdapBaseDN, ldap.baseDN) props.setProperty(LdapBaseDN, ldap.baseDN)
props.setProperty(LdapUserNameAttribute, ldap.userNameAttribute) props.setProperty(LdapUserNameAttribute, ldap.userNameAttribute)
ldap.fullNameAttribute.foreach(x => props.setProperty(LdapFullNameAttribute, x))
props.setProperty(LdapMailAddressAttribute, ldap.mailAttribute) props.setProperty(LdapMailAddressAttribute, ldap.mailAttribute)
ldap.tls.foreach(x => props.setProperty(LdapTls, x.toString)) ldap.tls.foreach(x => props.setProperty(LdapTls, x.toString))
ldap.keystore.foreach(x => props.setProperty(LdapKeystore, x)) ldap.keystore.foreach(x => props.setProperty(LdapKeystore, x))
@@ -71,6 +72,7 @@ trait SystemSettingsService {
getOptionValue(props, LdapBindPassword, None), getOptionValue(props, LdapBindPassword, None),
getValue(props, LdapBaseDN, ""), getValue(props, LdapBaseDN, ""),
getValue(props, LdapUserNameAttribute, ""), getValue(props, LdapUserNameAttribute, ""),
getOptionValue(props, LdapFullNameAttribute, None),
getValue(props, LdapMailAddressAttribute, ""), getValue(props, LdapMailAddressAttribute, ""),
getOptionValue[Boolean](props, LdapTls, None), getOptionValue[Boolean](props, LdapTls, None),
getOptionValue(props, LdapKeystore, None))) getOptionValue(props, LdapKeystore, None)))
@@ -101,6 +103,7 @@ object SystemSettingsService {
bindPassword: Option[String], bindPassword: Option[String],
baseDN: String, baseDN: String,
userNameAttribute: String, userNameAttribute: String,
fullNameAttribute: Option[String],
mailAttribute: String, mailAttribute: String,
tls: Option[Boolean], tls: Option[Boolean],
keystore: Option[String]) keystore: Option[String])
@@ -134,6 +137,7 @@ object SystemSettingsService {
private val LdapBindPassword = "ldap.bind_password" 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 LdapFullNameAttribute = "ldap.fullname_attribute"
private val LdapMailAddressAttribute = "ldap.mail_attribute" private val LdapMailAddressAttribute = "ldap.mail_attribute"
private val LdapTls = "ldap.tls" private val LdapTls = "ldap.tls"
private val LdapKeystore = "ldap.keystore" private val LdapKeystore = "ldap.keystore"

View File

@@ -18,51 +18,49 @@ object LDAPUtil {
/** /**
* 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(LDAPUserInfo) 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, LDAPUserInfo] = {
bind( bind(
ldapSettings.host, host = ldapSettings.host,
ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort), port = ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort),
ldapSettings.bindDN.getOrElse(""), dn = ldapSettings.bindDN.getOrElse(""),
ldapSettings.bindPassword.getOrElse(""), password = ldapSettings.bindPassword.getOrElse(""),
ldapSettings.tls.getOrElse(false), tls = ldapSettings.tls.getOrElse(false),
ldapSettings.keystore.getOrElse("") keystore = ldapSettings.keystore.getOrElse(""),
) match { error = "System LDAP authentication failed."
case Some(conn) => { ){ conn =>
withConnection(conn) { conn =>
findUser(conn, userName, ldapSettings.baseDN, ldapSettings.userNameAttribute) match { findUser(conn, userName, ldapSettings.baseDN, ldapSettings.userNameAttribute) match {
case Some(userDN) => userAuthentication(ldapSettings, userDN, password) case Some(userDN) => userAuthentication(ldapSettings, userDN, userName, password)
case None => Left("User does not exist.") 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] = { private def userAuthentication(ldapSettings: Ldap, userDN: String, userName: String, password: String): Either[String, LDAPUserInfo] = {
bind( bind(
ldapSettings.host, host = ldapSettings.host,
ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort), port = ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort),
userDN, dn = userDN,
password, password = password,
ldapSettings.tls.getOrElse(false), tls = ldapSettings.tls.getOrElse(false),
ldapSettings.keystore.getOrElse("") keystore = ldapSettings.keystore.getOrElse(""),
) match { error = "User LDAP Authentication Failed."
case Some(conn) => { ){ conn =>
withConnection(conn) { conn =>
findMailAddress(conn, userDN, ldapSettings.mailAttribute) match { findMailAddress(conn, userDN, ldapSettings.mailAttribute) match {
case Some(mailAddress) => Right(mailAddress) case Some(mailAddress) => Right(LDAPUserInfo(
userName = userName,
fullName = ldapSettings.fullNameAttribute.flatMap { fullNameAttribute =>
findFullName(conn, userDN, fullNameAttribute)
}.getOrElse(userName),
mailAddress = mailAddress))
case None => Left("Can't find mail address.") 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, tls: Boolean, keystore: String): Option[LDAPConnection] = { private def bind[A](host: String, port: Int, dn: String, password: String, tls: Boolean, keystore: String, error: String)
(f: LDAPConnection => Either[String, A]): Either[String, A] = {
if (tls) { if (tls) {
// Dynamically set Sun as the security provider // Dynamically set Sun as the security provider
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()) Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider())
@@ -87,7 +85,9 @@ object LDAPUtil {
// Bind to the server // Bind to the server
conn.bind(LDAP_VERSION, dn, password.getBytes) conn.bind(LDAP_VERSION, dn, password.getBytes)
Some(conn) // Execute a given function and returns a its result
f(conn)
} catch { } catch {
case e: Exception => { case e: Exception => {
// Provide more information if something goes wrong // Provide more information if something goes wrong
@@ -96,20 +96,15 @@ object LDAPUtil {
if (conn.isConnected) { if (conn.isConnected) {
conn.disconnect() conn.disconnect()
} }
// Returns an error message
None Left(error)
} }
} }
} }
private def withConnection[T](conn: LDAPConnection)(f: LDAPConnection => T): T = { /**
try { * Search a specified user and returns userDN if exists.
f(conn) */
} finally {
conn.disconnect()
}
}
private def findUser(conn: LDAPConnection, userName: String, baseDN: String, userNameAttribute: String): Option[String] = { private def findUser(conn: LDAPConnection, userName: String, baseDN: String, userNameAttribute: String): Option[String] = {
@tailrec @tailrec
def getEntries(results: LDAPSearchResults, entries: List[Option[LDAPEntry]] = Nil): List[LDAPEntry] = { def getEntries(results: LDAPSearchResults, entries: List[Option[LDAPEntry]] = Nil): List[LDAPEntry] = {
@@ -134,4 +129,14 @@ object LDAPUtil {
Option(results.next.getAttribute(mailAttribute)).map(_.getStringValue) Option(results.next.getAttribute(mailAttribute)).map(_.getStringValue)
} else None } else None
} }
private def findFullName(conn: LDAPConnection, userDN: String, nameAttribute: String): Option[String] =
defining(conn.search(userDN, LDAPConnection.SCOPE_BASE, null, Array[String](nameAttribute), false)){ results =>
if(results.hasMore) {
Option(results.next.getAttribute(nameAttribute)).map(_.getStringValue)
} else None
}
case class LDAPUserInfo(userName: String, fullName: String, mailAddress: String)
} }

View File

@@ -94,6 +94,13 @@
<span id="error-ldap_userNameAttribute" class="error"></span> <span id="error-ldap_userNameAttribute" class="error"></span>
</div> </div>
</div> </div>
<div class="control-group">
<label class="control-label" for="ldapFullNameAttribute">Full name attribute</label>
<div class="controls">
<input type="text" id="ldapFullNameAttribute" name="ldap.fullNameAttribute" value="@settings.ldap.map(_.fullNameAttribute)"/>
<span id="error-ldap_fullNameAttribute" class="error"></span>
</div>
</div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="ldapMailAttribute">Mail address attribute</label> <label class="control-label" for="ldapMailAttribute">Mail address attribute</label>
<div class="controls"> <div class="controls">