diff --git a/src/main/scala/app/SystemSettingsController.scala b/src/main/scala/app/SystemSettingsController.scala index b688d12..dc2065d 100644 --- a/src/main/scala/app/SystemSettingsController.scala +++ b/src/main/scala/app/SystemSettingsController.scala @@ -27,9 +27,11 @@ "ldap" -> optionalIfNotChecked("ldapAuthentication", mapping( "host" -> trim(label("LDAP host", text(required))), "port" -> trim(label("LDAP port", optional(number()))), - "baseDN" -> trim(label("BaseDN", text(required))), - "userNameAttribute" -> trim(label("User name attribute", text(required))), - "mailAttribute" -> trim(label("Mail address attribute", 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))), + "mailAttribute" -> trim(label("Mail address attribute", text(required))) )(Ldap.apply)) )(SystemSettings.apply) diff --git a/src/main/scala/service/SystemSettingsService.scala b/src/main/scala/service/SystemSettingsService.scala index a36e62b..4c01135 100644 --- a/src/main/scala/service/SystemSettingsService.scala +++ b/src/main/scala/service/SystemSettingsService.scala @@ -24,6 +24,8 @@ settings.ldap.map { ldap => props.setProperty(LdapHost, ldap.host) 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(LdapUserNameAttribute, ldap.userNameAttribute) props.setProperty(LdapMailAddressAttribute, ldap.mailAttribute) @@ -57,6 +59,8 @@ Some(Ldap( getValue(props, LdapHost, ""), getOptionValue(props, LdapPort, Some(DefaultLdapPort)), + getValue(props, LdapBindDN, ""), + getValue(props, LdapBindPassword, ""), getValue(props, LdapBaseDN, ""), getValue(props, LdapUserNameAttribute, ""), getValue(props, LdapMailAddressAttribute, ""))) @@ -82,6 +86,8 @@ case class Ldap( host: String, port: Option[Int], + bindDN: String, + bindPassword: String, baseDN: String, userNameAttribute: String, mailAttribute: String) @@ -106,6 +112,8 @@ private val LdapAuthentication = "ldap_authentication" private val LdapHost = "ldap.host" private val LdapPort = "ldap.port" + private val LdapBindDN = "ldap.bindDN" + private val LdapBindPassword = "ldap.bind_password" private val LdapBaseDN = "ldap.baseDN" private val LdapUserNameAttribute = "ldap.username_attribute" private val LdapMailAddressAttribute = "ldap.mail_attribute" diff --git a/src/main/scala/util/LDAPUtil.scala b/src/main/scala/util/LDAPUtil.scala index edd75fe..6ad1aa0 100644 --- a/src/main/scala/util/LDAPUtil.scala +++ b/src/main/scala/util/LDAPUtil.scala @@ -2,49 +2,107 @@ import service.SystemSettingsService.Ldap import service.SystemSettingsService -import com.novell.ldap.LDAPConnection +import com.novell.ldap.{LDAPReferralException, LDAPEntry, LDAPConnection} /** * Utility for LDAP authentication. */ -object LDAPUtil extends App { +object LDAPUtil { + + private val LDAP_VERSION: Int = 3 /** * Try authentication by LDAP using given configuration. * Returns Right(mailAddress) if authentication is successful, otherwise Left(errorMessage). */ def authenticate(ldapSettings: Ldap, userName: String, password: String): Either[String, String] = { - var conn: LDAPConnection = null - try { - conn = new LDAPConnection() - conn.connect(ldapSettings.host, ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort)) - val userDN = ldapSettings.userNameAttribute + "=" + userName + ",ou=Users," + ldapSettings.baseDN - 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 + 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") + } } - if(mailAddress != null){ - Right(mailAddress) - } else { - Left("Can't find mail address.") - } - } else { - Left("Authentication failed.") } + 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 { + conn.connect(host, port) + conn.bind(LDAP_VERSION, dn, password.getBytes) + Some(conn) } catch { - case ex: Exception => Left(ex.getMessage) - } finally { - if(conn != null){ - conn.disconnect() + case e: Exception => { + if (conn.isConnected) conn.disconnect() + None } } } -// val ldapSettings = Ldap("192.168.159.128", 389, "dc=unix-power,dc=net", "uid", "mail") -// -// println(authenticate(ldapSettings, "tanaka", "password")) + private def withConnection[T](conn: LDAPConnection)(f: LDAPConnection => T): T = { + try { + f(conn) + } finally { + 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 + } + + private def findMailAddress(conn: LDAPConnection, userDN: String, mailAttribute: String): Option[String] = { + val attributes = Array[String](mailAttribute) + 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 + } + } } diff --git a/src/main/twirl/admin/system.scala.html b/src/main/twirl/admin/system.scala.html index daebbc7..40be7bf 100644 --- a/src/main/twirl/admin/system.scala.html +++ b/src/main/twirl/admin/system.scala.html @@ -60,7 +60,22 @@
- + +
+ + +
+
+
+ +
+ + +
+
+ +
+