blob: 1cc5ba58a6118553c7529139d9b2795836c1babd [file] [log] [blame]
---
layout: post
status: PUBLISHED
published: true
title: LDAP authentication via Lift API in Apache ESME
id: 66ec157d-e700-43b6-b4c2-719a731e2948
date: '2011-05-26 14:35:06 -0400'
categories: esme
tags:
- esme
- authentication
- ldap
permalink: esme/entry/ldap_authentication_via_lift_api
---
<div style="background-color: transparent; margin-top: 0px; margin-left: 0px; margin-bottom: 0px; margin-right: 0px; ">
<div style="background-color: transparent; margin-top: 0px; margin-left: 0px; margin-bottom: 0px; margin-right: 0px; ">
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "><span style="white-space: normal; ">This blog was written by our new committer&nbsp;<i>Vladimir Ivanov</i>&nbsp;who implemented a feature that users have been wanting for a long time.&nbsp;</span></span></p>
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">In the first two parts of this blog series, scenarios were discussed where the authentication process was performed by the container. While it often makes sense to delegate this task to the container in order to integrate with corporate services such as Single Sign-On, sometimes it might be better to take full control of the authentication process and perform this task directly in the source code. In this final part of the blog series, I'll show how to authenticate user in LDAP via the Lift API as well as introduce some changes that have been made in the authentication modules.</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<h2><span style="background-color: transparent; font-weight: normal; font-style: normal; vertical-align: baseline; white-space: pre-wrap; ">Changes in UserAuth.scala</span></h2>
<p><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">In order to perform authentication via the Lift API, a new module </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">LDAPAuthModule</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> has been added. Basically, it uses the same operation sequence as the </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">ContainerManagedAuthModule: </span><span style="background-color: transparent; font-weight: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">it </span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">gets the user credentials, tries to authenticate and authorize the user, retrieves additional attributes from the LDAP for a new user and finally logs the user in. Since several operations are common to both authentication modules, it is worth placing them in a base trait </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">LDAPBase</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">:</span><br /><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">trait LDAPBase {</span></pre>
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">LDAPBase</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> is marked with a self-type to denote that any concrete class that mixes with this trait is a </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">AuthModule</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> instance</span><br /><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; this : AuthModule =></span></pre>
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">To check whether the user has a specific role, a list of roles, separated by commas, is read from the property file, split and placed into a val.</span><br /><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; val rolesToCheck = Props.get("role_list") match {
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp; case Full(s) => s.split(',').toList
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp; case _ => Nil
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; }
</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span></pre>
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">A new variable was also added to hold the current role for the User.</span><br /><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; var currentRole : String = _</span></pre>
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">The object </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">LDAPVendor</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> and method that extracts attributes from LDAP are used by both modules and were left without changes</span><br /><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; object myLdapVendor extends LDAPVendor
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; def myLdap : LDAPVendor = ...
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; def getAttrs(dn : String) : Map[String, List[String]] = ...</span></pre>
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">The following two methods are used to construct distinguished name for the user/group pair. Since users and groups can have different bases and prefixes, specific properties are retrieved from the property file depending on the </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">isGroup</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> flag (The new feature, default parameters, introduced in Scala 2.8 is used to set default value for this flag).</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; def constructDistinguishedName(who : String, isGroup : Boolean = false) = {
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp; val base = Props.get( if(isGroup) {"ldap.groupBase"} else {"ldap.userBase"} )&nbsp; openOr ""
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp; val dn = "%s,%s".format(constructNameWithPrefix(who, isGroup), base)
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp; dn
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; }
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; def constructNameWithPrefix(username: String, isGroup: Boolean = false) = {
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp; val prefix = if(isGroup) {"cn"} else {Props.get("ldap.uidPrefix") openOr ""}
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp; val nameWithPrefix = "%s=%s".format(prefix, username)
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp; nameWithPrefix
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; }</span></pre>
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">The method </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">logInUser </span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">was modified to set the current role for the authenticated User.</span><br /><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; def logInUser(who: User) {
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp; User.logUserIn(who)
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp; User.setRole(currentRole)
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp; S.notice(S.?("base_user_msg_welcome", who.niceName))
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; }
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">}</span></pre>
<p><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">The singleton object </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">ContainerManagedAuthModule </span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">now mixes with the </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">LDAPBase</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> trait and uses it's </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">constructDistinguishedName</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> method to get</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> the </span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">LDAP attributes. In all other aspects, it has the same implementation as before.</span><br /><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">object ContainerManagedAuthModule extends AuthModule with LDAPBase
</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">...</span></pre>
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">Now let's review the new </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">LDAPAuthModule.</span><br /><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">object LDAPAuthModule extends AuthModule with LDAPBase {</span></pre>
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">At first, a set of standard methods for all authentication modules is defined:</span><br /><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; override def isDefault = false
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; def loginPresentation: Box[NodeSeq] = ...
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; def moduleName: String = "ldap"
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; def createHolder() = ...</span></pre>
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">The method </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">performInit </span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">makes most of the module's work:</span><br /><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; def performInit(): Unit = {</span></pre>
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">The new module is mapped to the </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">/ldap/login</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> URL.</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp; LiftRules.dispatch.append {
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case Req("ldap" :: "login" :: Nil, _, PostRequest) =>
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; val from = S.referer openOr "/"</span></pre>
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">It is necessary to check whether LDAP is enabled based on the setting in the property file and if that's the case, the user credentials are read from the HTTP request and used for the subsequent authentication in LDAP with the </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">LDAPVendor.bindUser</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> method. It takes the distinguished name (composed of username that comes from the HTTP request and the prefix/user base taken from the property file) and password and returns a </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">Boolean</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">. The next step is to check if the user is authorized with the </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">checkRoles</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> method.</span><br /><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; val ldapEnabled = Props.getBool("ldap.enabled") openOr false
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(ldapEnabled) {
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; val name = S.param("username").map(_.trim.toLowerCase) openOr ""
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; val pwd = S.param("password").map(_.trim) openOr ""
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(myLdap.bindUser(constructNameWithPrefix(name), pwd) &amp;&amp; checkRoles(constructDistinguishedName(name))) {</span></pre>
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">After successful authentication and authorization, an attempt is made to find the existing User. If found, the User is logged in into the application. Otherwise, a new instance of User class is created, populated with attributes from LDAP, saved into the database and then logged in.</span><br /><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (for {
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; user <- UserAuth.find(By(UserAuth.authKey, name),
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; By(UserAuth.authType, moduleName)).flatMap(_.user.obj) or
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; User.find(By(User.nickname, name))
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } yield user) match {
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case Full(user) =>
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logInUser(user)
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case Empty =>
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; val usr = User.createAndPopulate.nickname(name).saveMe
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; val ldapAttrs = getAttrs(constructDistinguishedName(name))
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; val firstName = ldapAttrs("givenName").head
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; val lastName = ldapAttrs("sn").head
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; val mail = ldapAttrs("mail").head
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; usr.firstName(firstName).lastName(lastName).save
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UserAuth.create.authType(moduleName).user(usr).authKey(name).save
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logInUser(usr)
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S.error(S.?("base_user_err_unknown_creds"))
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; S.redirectTo(from)
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp; }
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; }</span></pre>
<p><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">The method </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">checkRoles </span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">takes the username as a parameter and iterates through a list of roles defined in the property file to see whether the current role contains this name as a value of its </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">uniqueMember </span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">attribute. If that's the case, it assign this role to the </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">currentRole</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> var of </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">LDAPBase</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> trait (it will be then saved in the HTTP session) and returns </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">true</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">, otherwise it returns </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">false.</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">def checkRoles(who : String) : Boolean = {
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp; for (role <-rolesToCheck) {
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; val ldapAttrs = getAttrs(constructDistinguishedName(role, true))
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; val uniqueMember = ldapAttrs("uniqueMember").head
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(who == uniqueMember) {
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; currentRole = role
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp; }
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp;&nbsp;&nbsp; return false;
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">&nbsp; }</span></pre>
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">The last thing I'm going to mention are the two new properties in the </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">default.props</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> file. The property </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">ldap.groupBase </span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">specifies the path in LDAP under which groups are searched. The list of application roles is set in the </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">role_list</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> property as a string separated by commas.</span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; ">default.props</span><br /><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<pre><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">;Group base DN to check whether user has specific role
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">ldap.groupBase=ou=Groups,ou=esme,dc=lester,dc=org
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">;Allow access to application for following roles
</span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">role_list=esme-users,monitoring-admin</span></pre>
<p><span style="background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">Note that the </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">lift-ldap </span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">module also has to be added as a dependency to the Maven </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">pom.xml</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> and/or the SBT project file to work with the Lift LDAP API.</span><span style="background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span> </p>
<h2><span style="background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">Conclusion</span></h2>
<p><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">In this article, we have discovered how to authenticate the User directly in LDAP using the Lift API as well as the corresponding changes in the </span><span style="background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">UserAuth</span><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> class.</span><br /><span style="background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">Links</span><br /><span style="background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "> </span><br /><span style="background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; ">Lift API: </span><a href="http://scala-tools.org/mvnsites/liftweb-2.3/" style="white-space: normal; "><span style="background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; ">http://scala-tools.org/mvnsites/liftweb-2.3/</span></a> </p>
</p></div>
</p></div>