diff --git a/src/main/scala/app/IndexController.scala b/src/main/scala/app/IndexController.scala index 2f5f689..a71c1a1 100644 --- a/src/main/scala/app/IndexController.scala +++ b/src/main/scala/app/IndexController.scala @@ -56,6 +56,11 @@ session.setAttribute(Keys.Session.LoginAccount, account) updateLastLoginDate(account.userName) + if(AccountUtil.hasLdapDummyMailAddress(account)) { + session.remove(Keys.Session.Redirect) + redirect("/" + account.userName + "/_edit") + } + session.getAndRemove[String](Keys.Session.Redirect).map { redirectUrl => if(redirectUrl.replaceFirst("/$", "") == request.getContextPath){ redirect("/") diff --git a/src/main/scala/app/SystemSettingsController.scala b/src/main/scala/app/SystemSettingsController.scala index d6dd787..b9707c0 100644 --- a/src/main/scala/app/SystemSettingsController.scala +++ b/src/main/scala/app/SystemSettingsController.scala @@ -7,7 +7,7 @@ import org.scalatra.FlashMapSupport class SystemSettingsController extends SystemSettingsControllerBase - with SystemSettingsService with AccountService with AdminAuthenticator +with SystemSettingsService with AccountService with AdminAuthenticator trait SystemSettingsControllerBase extends ControllerBase with FlashMapSupport { self: SystemSettingsService with AccountService with AdminAuthenticator => @@ -17,26 +17,28 @@ "gravatar" -> trim(label("Gravatar", boolean())), "notification" -> trim(label("Notification", boolean())), "smtp" -> optionalIfNotChecked("notification", mapping( - "host" -> trim(label("SMTP Host", text(required))), - "port" -> trim(label("SMTP Port", optional(number()))), - "user" -> trim(label("SMTP User", optional(text()))), - "password" -> trim(label("SMTP Password", optional(text()))), - "ssl" -> trim(label("Enable SSL", optional(boolean()))), - "fromAddress" -> trim(label("FROM Address", optional(text()))), - "fromName" -> trim(label("FROM Name", optional(text()))) + "host" -> trim(label("SMTP Host", text(required))), + "port" -> trim(label("SMTP Port", optional(number()))), + "user" -> trim(label("SMTP User", optional(text()))), + "password" -> trim(label("SMTP Password", optional(text()))), + "ssl" -> trim(label("Enable SSL", optional(boolean()))), + "fromAddress" -> trim(label("FROM Address", optional(text()))), + "fromName" -> trim(label("FROM Name", optional(text()))) )(Smtp.apply)), "ldapAuthentication" -> trim(label("LDAP", boolean())), "ldap" -> optionalIfNotChecked("ldapAuthentication", mapping( - "host" -> trim(label("LDAP host", text(required))), - "port" -> trim(label("LDAP port", optional(number()))), - "bindDN" -> trim(label("Bind DN", optional(text()))), - "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()))) + "host" -> trim(label("LDAP host", text(required))), + "port" -> trim(label("LDAP port", optional(number()))), + "bindDN" -> trim(label("Bind DN", optional(text()))), + "bindPassword" -> trim(label("Bind Password", optional(text()))), + "baseDN" -> trim(label("Base DN", text(required))), + "userNameAttribute" -> trim(label("User name attribute", text(required))), + "additionalFilterCondition"-> trim(label("Additional filter condition", optional(text()))), + "fullNameAttribute" -> trim(label("Full name attribute", optional(text()))), + "mailAttribute" -> trim(label("Mail address attribute", text(required))), + "disableMailResolve" -> trim(label("Disable Mail Resolve", optional(boolean()))), + "tls" -> trim(label("Enable TLS", optional(boolean()))), + "keystore" -> trim(label("Keystore", optional(text()))) )(Ldap.apply)) )(SystemSettings.apply) diff --git a/src/main/scala/service/AccountService.scala b/src/main/scala/service/AccountService.scala index 84e78a1..54a705e 100644 --- a/src/main/scala/service/AccountService.scala +++ b/src/main/scala/service/AccountService.scala @@ -39,7 +39,13 @@ case Right(ldapUserInfo) => { // Create or update account by LDAP information getAccountByUserName(userName, true) match { - case Some(x) if(!x.isRemoved) => updateAccount(x.copy(mailAddress = ldapUserInfo.mailAddress, fullName = ldapUserInfo.fullName)) + case Some(x) if(!x.isRemoved) => { + if(settings.ldap.get.disableMailResolve.getOrElse(false)) { + updateAccount(x.copy(fullName = ldapUserInfo.fullName)) + } else { + updateAccount(x.copy(mailAddress = ldapUserInfo.mailAddress, fullName = ldapUserInfo.fullName)) + } + } case Some(x) if(x.isRemoved) => { logger.info(s"LDAP Authentication Failed: Account is already registered but disabled..") defaultAuthentication(userName, password) diff --git a/src/main/scala/service/SystemSettingsService.scala b/src/main/scala/service/SystemSettingsService.scala index 5514cbe..8d1e82c 100644 --- a/src/main/scala/service/SystemSettingsService.scala +++ b/src/main/scala/service/SystemSettingsService.scala @@ -31,8 +31,10 @@ ldap.bindPassword.foreach(x => props.setProperty(LdapBindPassword, x)) props.setProperty(LdapBaseDN, ldap.baseDN) props.setProperty(LdapUserNameAttribute, ldap.userNameAttribute) + ldap.additionalFilterCondition.foreach(x => props.setProperty(LdapAdditionalFilterCondition, x)) ldap.fullNameAttribute.foreach(x => props.setProperty(LdapFullNameAttribute, x)) props.setProperty(LdapMailAddressAttribute, ldap.mailAttribute) + ldap.disableMailResolve.foreach(x => props.setProperty(LdapDisableMailResolve, x.toString)) ldap.tls.foreach(x => props.setProperty(LdapTls, x.toString)) ldap.keystore.foreach(x => props.setProperty(LdapKeystore, x)) } @@ -72,8 +74,10 @@ getOptionValue(props, LdapBindPassword, None), getValue(props, LdapBaseDN, ""), getValue(props, LdapUserNameAttribute, ""), + getOptionValue(props, LdapAdditionalFilterCondition, None), getOptionValue(props, LdapFullNameAttribute, None), getValue(props, LdapMailAddressAttribute, ""), + getOptionValue[Boolean](props, LdapDisableMailResolve, None), getOptionValue[Boolean](props, LdapTls, None), getOptionValue(props, LdapKeystore, None))) } else { @@ -89,33 +93,35 @@ import scala.reflect.ClassTag case class SystemSettings( - allowAccountRegistration: Boolean, - gravatar: Boolean, - notification: Boolean, - smtp: Option[Smtp], - ldapAuthentication: Boolean, - ldap: Option[Ldap]) + allowAccountRegistration: Boolean, + gravatar: Boolean, + notification: Boolean, + smtp: Option[Smtp], + ldapAuthentication: Boolean, + ldap: Option[Ldap]) case class Ldap( - host: String, - port: Option[Int], - bindDN: Option[String], - bindPassword: Option[String], - baseDN: String, - userNameAttribute: String, - fullNameAttribute: Option[String], - mailAttribute: String, - tls: Option[Boolean], - keystore: Option[String]) + host: String, + port: Option[Int], + bindDN: Option[String], + bindPassword: Option[String], + baseDN: String, + userNameAttribute: String, + additionalFilterCondition: Option[String], + fullNameAttribute: Option[String], + mailAttribute: String, + disableMailResolve: Option[Boolean], + tls: Option[Boolean], + keystore: Option[String]) case class Smtp( - host: String, - port: Option[Int], - user: Option[String], - password: Option[String], - ssl: Option[Boolean], - fromAddress: Option[String], - fromName: Option[String]) + host: String, + port: Option[Int], + user: Option[String], + password: Option[String], + ssl: Option[Boolean], + fromAddress: Option[String], + fromName: Option[String]) val DefaultSmtpPort = 25 val DefaultLdapPort = 389 @@ -137,8 +143,10 @@ private val LdapBindPassword = "ldap.bind_password" private val LdapBaseDN = "ldap.baseDN" private val LdapUserNameAttribute = "ldap.username_attribute" + private val LdapAdditionalFilterCondition = "ldap.additional_filter_condition" private val LdapFullNameAttribute = "ldap.fullname_attribute" private val LdapMailAddressAttribute = "ldap.mail_attribute" + private val LdapDisableMailResolve = "ldap.disable_mail_resolve" private val LdapTls = "ldap.tls" private val LdapKeystore = "ldap.keystore" diff --git a/src/main/scala/util/AccountUtil.scala b/src/main/scala/util/AccountUtil.scala new file mode 100644 index 0000000..e6da429 --- /dev/null +++ b/src/main/scala/util/AccountUtil.scala @@ -0,0 +1,18 @@ +package util + +import model.Account + +/** + * Utility for account model. + */ +object AccountUtil { + private val LDAP_DUMMY_MAL = "@ldap-devnull" + + def hasLdapDummyMailAddress(account: Account): Boolean = { + account.mailAddress.endsWith(LDAP_DUMMY_MAL) + } + + def getLdapDummyMailAddress(userName: String): String = { + userName + LDAP_DUMMY_MAL + } +} diff --git a/src/main/scala/util/LDAPUtil.scala b/src/main/scala/util/LDAPUtil.scala index 05a7171..e0d42ad 100644 --- a/src/main/scala/util/LDAPUtil.scala +++ b/src/main/scala/util/LDAPUtil.scala @@ -30,7 +30,7 @@ keystore = ldapSettings.keystore.getOrElse(""), error = "System LDAP authentication failed." ){ conn => - findUser(conn, userName, ldapSettings.baseDN, ldapSettings.userNameAttribute) match { + findUser(conn, userName, ldapSettings.baseDN, ldapSettings.userNameAttribute, ldapSettings.additionalFilterCondition) match { case Some(userDN) => userAuthentication(ldapSettings, userDN, userName, password) case None => Left("User does not exist.") } @@ -47,20 +47,29 @@ keystore = ldapSettings.keystore.getOrElse(""), error = "User LDAP Authentication Failed." ){ conn => - findMailAddress(conn, userDN, ldapSettings.mailAttribute) match { - case Some(mailAddress) => Right(LDAPUserInfo( + if(ldapSettings.disableMailResolve.getOrElse(false)) { + 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.") + mailAddress = AccountUtil.getLdapDummyMailAddress(userName))) + } else { + 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.") + } } } } 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] = { + (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()) @@ -105,7 +114,7 @@ /** * Search a specified user and returns userDN if exists. */ - private def findUser(conn: LDAPConnection, userName: String, baseDN: String, userNameAttribute: String): Option[String] = { + private def findUser(conn: LDAPConnection, userName: String, baseDN: String, userNameAttribute: String, additionalFilterCondition: Option[String]): Option[String] = { @tailrec def getEntries(results: LDAPSearchResults, entries: List[Option[LDAPEntry]] = Nil): List[LDAPEntry] = { if(results.hasMore){ @@ -118,7 +127,13 @@ entries.flatten } } - getEntries(conn.search(baseDN, LDAPConnection.SCOPE_SUB, userNameAttribute + "=" + userName, null, false)).collectFirst { + + val filterCond = additionalFilterCondition.getOrElse("") match { + case "" => userNameAttribute + "=" + userName + case x => "(&(" + x + ")(" + userNameAttribute + "=" + userName + "))" + } + + getEntries(conn.search(baseDN, LDAPConnection.SCOPE_SUB, filterCond, null, false)).collectFirst { case x => x.getDN } } diff --git a/src/main/scala/util/Notifier.scala b/src/main/scala/util/Notifier.scala index 21dfce6..d7e13a7 100644 --- a/src/main/scala/util/Notifier.scala +++ b/src/main/scala/util/Notifier.scala @@ -27,7 +27,7 @@ ) .distinct .withFilter ( _ != context.loginAccount.get.userName ) // the operation in person is excluded - .foreach ( getAccountByUserName(_) filterNot (_.isGroupAccount) foreach (x => notify(x.mailAddress)) ) + .foreach ( getAccountByUserName(_) filterNot (_.isGroupAccount) filterNot (AccountUtil.hasLdapDummyMailAddress(_)) foreach (x => notify(x.mailAddress)) ) } diff --git a/src/main/twirl/account/edit.scala.html b/src/main/twirl/account/edit.scala.html index 5213ddc..670411d 100644 --- a/src/main/twirl/account/edit.scala.html +++ b/src/main/twirl/account/edit.scala.html @@ -1,13 +1,17 @@ @(account: Option[model.Account], info: Option[Any])(implicit context: app.Context) @import context._ @import view.helpers._ +@import util.AccountUtil @html.main((if(account.isDefined) "Edit your profile" else "Create your account")){ +@helper.html.information(info) + @if(account.isDefined && AccountUtil.hasLdapDummyMailAddress(account.get)) { +