Completed ESME-370: Refactor LDAP auth modules and related operations
git-svn-id: https://svn.apache.org/repos/asf/esme/trunk@1226313 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/server/src/main/scala/org/apache/esme/model/UserAuth.scala b/server/src/main/scala/org/apache/esme/model/UserAuth.scala
index 551d0b5..99f3776 100644
--- a/server/src/main/scala/org/apache/esme/model/UserAuth.scala
+++ b/server/src/main/scala/org/apache/esme/model/UserAuth.scala
@@ -49,8 +49,10 @@
import scala.xml._
-import scalaz.{OptionW, Scalaz}
+import scalaz.{OptionW, Identity, IterV, Input, State, Scalaz}
+import scalaz.effects.{IO}
import Scalaz._
+import IterV.{Empty => EmptyZ, Done => DoneZ, _}
import org.apache.esme.actor._
@@ -326,12 +328,13 @@
myLdapVendor
}
+ @deprecated
def getAttrs(dn : String) : Map[String, List[String]] = {
var attrsMap = Map.empty[String, List[String]]
- val attrs : Attributes = myLdap.attributesFromDn(dn)
- if (attrs != null) {
- val allAttrs = attrs.getAll();
- if (allAttrs != null) {
+ val attributes = getLDAPAttributes(dn)
+ attributes.foreach(attrs => {
+ val allAttributes = Option(attrs.getAll());
+ allAttributes.foreach(allAttrs => {
while(allAttrs.hasMore()) {
val attribute = allAttrs.next().asInstanceOf[Attr];
debug("Attribute name: '%s', has following values:".format(attribute.getID()))
@@ -342,9 +345,69 @@
}
attrsMap += (attribute.getID() -> attrValues)
}
+ })
+ })
+ attrsMap
+ }
+
+ def getLDAPAttributes(dn: String): Option[Attributes] = {
+ //TODO: it's probably better to return Either or Validation
+ try {
+ // Ugly, but following operation
+ // throws NamingException in case
+ // given DN doesn't exist in LDAP
+ Option(myLdap.attributesFromDn(dn))
+ } catch {
+ case ne: javax.naming.NamingException => {
+ info("Naming Exception was thrown: %s".format(ne))
+ none
}
}
- attrsMap
+ }
+
+ // Scalaz Iteratee was used
+ // returns pairs name->list of values
+ def getAttributesMap(dn: String) : Option[Map[String, List[String]]] = {
+ val attributes = getLDAPAttributes(dn)
+ attributes.flatMap(attrs => {
+ val allAttributes = Option(attrs.getAll());
+ allAttributes.map(allAttrs => {
+ val res = enumNamingEnumeration(allAttrs, accum, get) map (_.run)
+ res.unsafePerformIO ~> Map.empty[String, List[String]]
+ })
+ })
+ }
+
+
+ def enumNamingEnumeration[E, A, T <: Attr](ne: NamingEnumeration[T], iter: IterV[E, A], constr: NamingEnumeration[T] => IO[E]): IO[IterV[E, A]] = {
+ def loop(i: IterV[E, A]): IO[IterV[E, A]] =
+ i.fold(done = (_, _) => i.pure[IO],
+ cont = k => next(ne) >>= (hasMore =>
+ if (!hasMore) i.pure[IO]
+ else constr(ne) >>= (t => loop(k(El(t))))))
+ loop(iter)
+ }
+
+ def next: NamingEnumeration[_ <: Attr] => IO[Boolean] = _.hasMore().pure[IO]
+
+ def get: NamingEnumeration[_ <: Attr] => IO[(String, List[String])] = ne => {
+ val attribute = ne.next().asInstanceOf[Attr];
+ debug("Attribute name: '%s', has following values:".format(attribute.getID()))
+ var attrValues = List.empty[String]
+ for(i <- 0 until attribute.size()) {
+ debug("Attribute value: '%s'".format(attribute.get(i)))
+ attrValues ::= attribute.get(i).toString
+ }
+ (attribute.getID() -> attrValues).pure[IO]
+ }
+
+ def accum: IterV[(String, List[String]), State[Map[String, List[String]], Unit]] = {
+ def step(s: State[Map[String, List[String]], Unit]): Input[(String, List[String])] => IterV[(String, List[String]), State[Map[String, List[String]], Unit]] = {
+ case El(x1, x2) => Cont(step(s.withs((_: Map[String, List[String]]) + (x1 -> x2))))
+ case EmptyZ() => Cont(step(s))
+ case EOF() => DoneZ(s, EOF[(String, List[String])])
+ }
+ Cont(step(state[Map[String, List[String]], Unit](s => (s, ()))))
}
def constructDistinguishedName(who : String, isGroup : Boolean = false) = {
@@ -391,48 +454,50 @@
val httpRequest: HTTPRequest = req.request
val hrs = httpRequest.asInstanceOf[HTTPRequestServlet]
val hsr: HttpServletRequest = hrs.req
- val username : String = hsr.getRemoteUser
+ val username = Option(hsr.getRemoteUser)
debug("Username: '%s'".format(username))
- if(username!=null){
- val currentRoles = rolesToCheck.filter(hsr.isUserInRole(_))
- debug("User from HTTP Request: %s has following roles=%s".format(username, currentRoles))
- if(currentRoles.size == 0) {
- debug("No roles have been found")
- S.error(S.?("base_user_err_unknown_creds"))
- } else {
- currentRoles.map(cr => {
- currentRole = cr
- (for {
- user <- UserAuth.find(By(UserAuth.authKey, username),
- By(UserAuth.authType, moduleName)).flatMap(_.user.obj) or
- User.find(By(User.nickname, username))
- } yield user) match {
- case Full(user) => {
- debug("User: '%s' has been found".format(user.niceName))
- logInUser(user)
- }
- case _ => {
- val usr = User.createAndPopulate.nickname(username).saveMe
- //find and save additional attributes in LDAP if it's enabled
- val ldapEnabled = Props.getBool("ldap.enabled") openOr false
- if(ldapEnabled) {
- val ldapAttrs = getAttrs(constructDistinguishedName(username))
- val firstName = ldapAttrs("givenName").head
- val lastName = ldapAttrs("sn").head
- val mail = ldapAttrs("mail").head
- debug("Attributes from LDAP for user '%s'. Firstname: '%s', lastname: '%s', email: '%s'".format(username, firstName, lastName, mail))
- usr.firstName(firstName).lastName(lastName).save
+ username.cata(uName => {
+ rolesToCheck.filter(hsr.isUserInRole(_)) match {
+ case Nil => {
+ debug("No roles have been found")
+ S.error(S.?("base_user_err_unknown_creds"))
+ }
+ case xs => {
+ debug("User from HTTP Request: %s has following roles=%s".format(uName, xs))
+ xs.map(cr => {
+ currentRole = cr
+ (for {
+ user <- UserAuth.find(By(UserAuth.authKey, uName),
+ By(UserAuth.authType, moduleName)).flatMap(_.user.obj) or
+ User.find(By(User.nickname, uName))
+ } yield user) match {
+ case Full(user) => {
+ debug("User: '%s' has been found".format(user.niceName))
+ logInUser(user)
}
- UserAuth.create.authType(moduleName).user(usr).authKey(username).save
- logInUser(usr)
+ case _ => {
+ val usr = User.createAndPopulate.nickname(uName).saveMe
+ //find and save additional attributes in LDAP if it's enabled
+ val ldapEnabled = Props.getBool("ldap.enabled") openOr false
+ if(ldapEnabled) {
+ getAttributesMap(constructDistinguishedName(uName)).map(ldapAttrs => {
+ ldapAttrs.get("givenName").flatMap(_.headOption.map(usr.firstName(_)))
+ ldapAttrs.get("sn").flatMap(_.headOption.map(usr.lastName(_)))
+ //TODO: There's no corresponding property in User's Mapper
+ //ldapAttrs.get("mail").flatMap(_.headOption.map(usr))
+ debug("Attributes from LDAP for user '%s'. Firstname: '%s', lastname: '%s'".format(uName, usr.firstName, usr.lastName))
+ usr.save
+ })
+ }
+ UserAuth.create.authType(moduleName).user(usr).authKey(uName).save
+ logInUser(usr)
+ }
}
- }
- })
+ })
+ }
}
- } else {
- S.error(S.?("base_user_err_unknown_creds"))
- }
-
+ },
+ S.error(S.?("base_user_err_unknown_creds")))
}
case Empty => {
S.error(S.?("base_user_err_unknown_creds"))
@@ -468,7 +533,7 @@
if(ldapEnabled) {
val name = S.param("username").map(_.trim.toLowerCase) openOr ""
val pwd = S.param("password").map(_.trim) openOr ""
- if(myLdap.bindUser(constructNameWithPrefix(name), pwd) && checkRoles(constructDistinguishedName(name))) {
+ if(myLdap.bindUser(constructNameWithPrefix(name), pwd) && !checkRoles(constructDistinguishedName(name)).isEmpty) {
(for {
user <- UserAuth.find(By(UserAuth.authKey, name),
By(UserAuth.authType, moduleName)).flatMap(_.user.obj) or
@@ -480,12 +545,14 @@
case Empty =>
val usr = User.createAndPopulate.nickname(name).saveMe
//find and save additional attributes in LDAP if it's enabled
- val ldapAttrs = getAttrs(constructDistinguishedName(name))
- val firstName = ldapAttrs("givenName").head
- val lastName = ldapAttrs("sn").head
- val mail = ldapAttrs("mail").head
- debug("Attributes from LDAP for user '%s'. Firstname: '%s', lastname: '%s', email: '%s'".format(name, firstName, lastName, mail))
- usr.firstName(firstName).lastName(lastName).save
+ getAttributesMap(constructDistinguishedName(name)).map(ldapAttrs => {
+ ldapAttrs.get("givenName").flatMap(_.headOption.map(usr.firstName(_)))
+ ldapAttrs.get("sn").flatMap(_.headOption.map(usr.lastName(_)))
+ //TODO: There's no corresponding property in User's Mapper
+ //ldapAttrs.get("mail").flatMap(_.headOption.map(usr))
+ debug("Attributes from LDAP for user '%s'. Firstname: '%s', lastname: '%s'".format(name, usr.firstName, usr.lastName))
+ usr.save
+ })
UserAuth.create.authType(moduleName).user(usr).authKey(name).save
logInUser(usr)
}
@@ -498,18 +565,23 @@
}
}
- def checkRoles(who : String) : Boolean = {
- for (role <-rolesToCheck) {
- val ldapAttrs = getAttrs(constructDistinguishedName(role, true))
- val uniqueMember = ldapAttrs("uniqueMember").head
- debug("'uniqueMember' attribute value: '%s'".format(uniqueMember))
- if(who == uniqueMember) {
- currentRole = role
- return true
- }
+ def checkRoles(who : String) : Option[String] = {
+ var matchedRoles: List[Option[String]] = Nil
+ for (role <- rolesToCheck) {
+ matchedRoles ::= getAttributesMap(constructDistinguishedName(role, true)).flatMap(ldapAttrs => {
+ ldapAttrs.get("uniqueMember").flatMap(_.headOption.flatMap(uniqueMember => {
+ if(who ≟ uniqueMember) {
+ currentRole = role
+ some(role)
+ } else {
+ none
+ }
+ }))
+ })
}
- debug("No roles have been found")
- return false;
+ // At the moment only one (first matched) role is taken into account
+ // therefore First Option Monoid was used
+ matchedRoles.map(_.fst).asMA.sum.value
}
def createHolder(): FieldSet = new FieldSet {