diff --git a/src/main/scala/app/SystemSettingsController.scala b/src/main/scala/app/SystemSettingsController.scala index 68efda5..d6dd787 100644 --- a/src/main/scala/app/SystemSettingsController.scala +++ b/src/main/scala/app/SystemSettingsController.scala @@ -33,6 +33,7 @@ "bindPassword" -> trim(label("Bind Password", optional(text()))), "baseDN" -> trim(label("Base DN", 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))), "tls" -> trim(label("Enable TLS", optional(boolean()))), "keystore" -> trim(label("Keystore", optional(text()))) diff --git a/src/main/scala/service/AccountService.scala b/src/main/scala/service/AccountService.scala index 905736b..006a9be 100644 --- a/src/main/scala/service/AccountService.scala +++ b/src/main/scala/service/AccountService.scala @@ -36,11 +36,11 @@ */ private def ldapAuthentication(settings: SystemSettings, userName: String, password: String) = { LDAPUtil.authenticate(settings.ldap.get, userName, password) match { - case Right(mailAddress) => { + case Right(ldapUserInfo) => { // Create or update account by LDAP information getAccountByUserName(userName) match { - case Some(x) => updateAccount(x.copy(mailAddress = mailAddress)) - case None => createAccount(userName, "", userName, mailAddress, false, None) + case Some(x) => updateAccount(x.copy(mailAddress = ldapUserInfo.mailAddress, fullName = ldapUserInfo.fullName)) + case None => createAccount(userName, "", ldapUserInfo.fullName, ldapUserInfo.mailAddress, false, None) } getAccountByUserName(userName) } diff --git a/src/main/scala/service/SystemSettingsService.scala b/src/main/scala/service/SystemSettingsService.scala index 85e94f7..5514cbe 100644 --- a/src/main/scala/service/SystemSettingsService.scala +++ b/src/main/scala/service/SystemSettingsService.scala @@ -31,6 +31,7 @@ ldap.bindPassword.foreach(x => props.setProperty(LdapBindPassword, x)) props.setProperty(LdapBaseDN, ldap.baseDN) props.setProperty(LdapUserNameAttribute, ldap.userNameAttribute) + ldap.fullNameAttribute.foreach(x => props.setProperty(LdapFullNameAttribute, x)) props.setProperty(LdapMailAddressAttribute, ldap.mailAttribute) ldap.tls.foreach(x => props.setProperty(LdapTls, x.toString)) ldap.keystore.foreach(x => props.setProperty(LdapKeystore, x)) @@ -71,6 +72,7 @@ getOptionValue(props, LdapBindPassword, None), getValue(props, LdapBaseDN, ""), getValue(props, LdapUserNameAttribute, ""), + getOptionValue(props, LdapFullNameAttribute, None), getValue(props, LdapMailAddressAttribute, ""), getOptionValue[Boolean](props, LdapTls, None), getOptionValue(props, LdapKeystore, None))) @@ -101,6 +103,7 @@ bindPassword: Option[String], baseDN: String, userNameAttribute: String, + fullNameAttribute: Option[String], mailAttribute: String, tls: Option[Boolean], keystore: Option[String]) @@ -134,6 +137,7 @@ private val LdapBindPassword = "ldap.bind_password" private val LdapBaseDN = "ldap.baseDN" private val LdapUserNameAttribute = "ldap.username_attribute" + private val LdapFullNameAttribute = "ldap.fullname_attribute" private val LdapMailAddressAttribute = "ldap.mail_attribute" private val LdapTls = "ldap.tls" private val LdapKeystore = "ldap.keystore" diff --git a/src/main/scala/util/LDAPUtil.scala b/src/main/scala/util/LDAPUtil.scala index b6859d2..05a7171 100644 --- a/src/main/scala/util/LDAPUtil.scala +++ b/src/main/scala/util/LDAPUtil.scala @@ -18,51 +18,49 @@ /** * 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( - ldapSettings.host, - ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort), - ldapSettings.bindDN.getOrElse(""), - ldapSettings.bindPassword.getOrElse(""), - ldapSettings.tls.getOrElse(false), - ldapSettings.keystore.getOrElse("") - ) 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.") - } - } + host = ldapSettings.host, + port = ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort), + dn = ldapSettings.bindDN.getOrElse(""), + password = ldapSettings.bindPassword.getOrElse(""), + tls = ldapSettings.tls.getOrElse(false), + keystore = ldapSettings.keystore.getOrElse(""), + error = "System LDAP authentication failed." + ){ conn => + findUser(conn, userName, ldapSettings.baseDN, ldapSettings.userNameAttribute) match { + case Some(userDN) => userAuthentication(ldapSettings, userDN, userName, 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] = { + private def userAuthentication(ldapSettings: Ldap, userDN: String, userName: String, password: String): Either[String, LDAPUserInfo] = { bind( - ldapSettings.host, - ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort), - userDN, - password, - ldapSettings.tls.getOrElse(false), - ldapSettings.keystore.getOrElse("") - ) 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.") - } - } + host = ldapSettings.host, + port = ldapSettings.port.getOrElse(SystemSettingsService.DefaultLdapPort), + dn = userDN, + password = password, + tls = ldapSettings.tls.getOrElse(false), + keystore = ldapSettings.keystore.getOrElse(""), + error = "User LDAP Authentication Failed." + ){ conn => + findMailAddress(conn, userDN, ldapSettings.mailAttribute) match { + 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("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) { // Dynamically set Sun as the security provider Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()) @@ -87,7 +85,9 @@ // Bind to the server conn.bind(LDAP_VERSION, dn, password.getBytes) - Some(conn) + // Execute a given function and returns a its result + f(conn) + } catch { case e: Exception => { // Provide more information if something goes wrong @@ -96,20 +96,15 @@ if (conn.isConnected) { conn.disconnect() } - - None + // Returns an error message + Left(error) } } } - private def withConnection[T](conn: LDAPConnection)(f: LDAPConnection => T): T = { - try { - f(conn) - } finally { - conn.disconnect() - } - } - + /** + * Search a specified user and returns userDN if exists. + */ private def findUser(conn: LDAPConnection, userName: String, baseDN: String, userNameAttribute: String): Option[String] = { @tailrec def getEntries(results: LDAPSearchResults, entries: List[Option[LDAPEntry]] = Nil): List[LDAPEntry] = { @@ -134,4 +129,14 @@ Option(results.next.getAttribute(mailAttribute)).map(_.getStringValue) } 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) + } diff --git a/src/main/twirl/admin/system.scala.html b/src/main/twirl/admin/system.scala.html index 17e8062..1ccab70 100644 --- a/src/main/twirl/admin/system.scala.html +++ b/src/main/twirl/admin/system.scala.html @@ -95,6 +95,13 @@
+ +
+ + +
+
+