← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 20933: LDAP. Made LDAP authentcation more flexible through a user search with a base search and filter s...

 

Merge authors:
  Lars Helge Øverland (larshelge)
------------------------------------------------------------
revno: 20933 [merge]
committer: Lars Helge Overland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Tue 2015-11-03 10:01:16 -0500
message:
  LDAP. Made LDAP authentcation more flexible through a user search with a base search and filter string.
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserCredentials.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/DefaultUserDetailsService.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/ldap/authentication/DhisBindAuthenticator.java
  dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/security.xml
  dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/user/hibernate/UserCredentials.hbm.xml
  dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/ConfigurationKey.java
  dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/ConfigurationPropertyFactoryBean.java
  dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/DefaultDhisConfigurationProvider.java
  dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/security.xml
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/java/org/hisp/dhis/user/action/AddUserAction.java
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/java/org/hisp/dhis/user/action/UpdateUserAction.java
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/resources/org/hisp/dhis/user/i18n_module.properties
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/webapp/dhis-web-maintenance-user/addUserForm.vm
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/webapp/dhis-web-maintenance-user/updateUserForm.vm


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserCredentials.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserCredentials.java	2015-10-05 17:45:17 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserCredentials.java	2015-11-03 03:22:58 +0000
@@ -78,6 +78,11 @@
      * Unique OpenID.
      */
     private String openId;
+    
+    /**
+     * Unique LDAP distinguished name.
+     */
+    private String ldapId;
 
     /**
      * Required. Will be stored as a hash.
@@ -435,6 +440,14 @@
         return constraints != null && !constraints.isEmpty();
     }
 
+    /**
+     * Indicates whether the LDAP identifier is present.
+     */
+    public boolean hasLdapId()
+    {
+        return ldapId != null && !ldapId.isEmpty();
+    }
+    
     // -------------------------------------------------------------------------
     // hashCode and equals
     // -------------------------------------------------------------------------
@@ -593,6 +606,19 @@
     @JsonProperty
     @JsonView( { DetailedView.class, ExportView.class } )
     @JacksonXmlProperty( namespace = DxfNamespaces.DXF_2_0 )
+    public String getLdapId()
+    {
+        return ldapId;
+    }
+
+    public void setLdapId( String ldapId )
+    {
+        this.ldapId = ldapId;
+    }
+
+    @JsonProperty
+    @JsonView( { DetailedView.class, ExportView.class } )
+    @JacksonXmlProperty( namespace = DxfNamespaces.DXF_2_0 )
     public Date getLastLogin()
     {
         return lastLogin;

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/DefaultUserDetailsService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/DefaultUserDetailsService.java	2015-10-25 20:30:23 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/DefaultUserDetailsService.java	2015-11-03 04:47:50 +0000
@@ -40,9 +40,12 @@
     {
         UserCredentials credentials = userService.getUserCredentialsByUsername( username );
 
+        // ---------------------------------------------------------------------
+        // OpenId
+        // ---------------------------------------------------------------------
+
         if ( credentials == null )
         {
-            // TODO: try with openid identifier if username not found, we might want to refactor this into a OpenIDUserDetailsService.
             credentials = userService.getUserCredentialsByOpenID( username );
 
             if ( credentials == null )
@@ -51,6 +54,10 @@
             }
         }
 
+        // ---------------------------------------------------------------------
+        // UserDetails
+        // ---------------------------------------------------------------------
+
         boolean credentialsExpired = userService.credentialsNonExpired( credentials );
 
         return new User( credentials.getUsername(), credentials.getPassword(),

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/ldap/authentication/DhisBindAuthenticator.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/ldap/authentication/DhisBindAuthenticator.java	2015-10-27 18:06:21 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/security/ldap/authentication/DhisBindAuthenticator.java	2015-11-03 15:01:16 +0000
@@ -29,10 +29,13 @@
  */
 
 import org.hisp.dhis.external.conf.DhisConfigurationProvider;
+import org.hisp.dhis.user.UserCredentials;
+import org.hisp.dhis.user.UserService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.ldap.core.DirContextOperations;
 import org.springframework.ldap.core.support.BaseLdapPathContextSource;
 import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.ldap.authentication.BindAuthenticator;
 
@@ -49,6 +52,9 @@
     @Autowired
     private DhisConfigurationProvider configurationProvider;
     
+    @Autowired
+    private UserService userService;
+    
     public DhisBindAuthenticator( BaseLdapPathContextSource contextSource )
     {
         super( contextSource );
@@ -63,6 +69,18 @@
         {
             throw new BadCredentialsException( "LDAP authentication is not configured" );
         }
+
+        UserCredentials userCredentials = userService.getUserCredentialsByUsername( authentication.getName() );
+                
+        if ( userCredentials == null )
+        {
+            throw new BadCredentialsException( "Incorrect user credentials" );
+        }
+        
+        if ( userCredentials.hasLdapId() )
+        {
+            authentication = new UsernamePasswordAuthenticationToken( userCredentials.getLdapId(), authentication.getCredentials() );
+        }
         
         return super.authenticate( authentication );
     }

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/security.xml'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/security.xml	2015-11-02 17:34:22 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/security.xml	2015-11-03 14:57:20 +0000
@@ -41,7 +41,7 @@
     <constructor-arg>
       <bean class="org.hisp.dhis.security.ldap.authentication.DhisBindAuthenticator">
         <constructor-arg ref="contextSource"/>
-        <property name="userDnPatterns" ref="ldapDnPatterns" />
+        <property name="userSearch" ref="userSearch" />
       </bean>
     </constructor-arg>
     <constructor-arg>
@@ -51,17 +51,18 @@
     </constructor-arg>
   </bean>
   
-  <bean id="ldapUrl" class="org.hisp.dhis.external.conf.ConfigurationPropertyFactoryBean"><constructor-arg value="LDAP_URL" /></bean>
-  
-  <bean id="ldapManagerDn" class="org.hisp.dhis.external.conf.ConfigurationPropertyFactoryBean"><constructor-arg value="LDAP_MANAGER_DN" /></bean>
-  
-  <bean id="ldapManagerPassword" class="org.hisp.dhis.external.conf.ConfigurationPropertyFactoryBean"><constructor-arg value="LDAP_MANAGER_PASSWORD" /></bean>
-    
-  <bean id="ldapDnPatterns" class="org.hisp.dhis.external.conf.ConfigurationPropertyFactoryBean">
-    <constructor-arg value="LDAP_DN_PATTERNS" />
-    <property name="list" value="true" />
-  </bean>  
-    
+  <bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
+    <constructor-arg index="0" ref="ldapSearchBase" />
+    <constructor-arg index="1" ref="ldapSearchFilter" />
+    <constructor-arg index="2" ref="contextSource" />
+  </bean>
+
+  <bean id="ldapUrl" class="org.hisp.dhis.external.conf.ConfigurationPropertyFactoryBean"><constructor-arg value="LDAP_URL" /></bean>  
+  <bean id="ldapManagerDn" class="org.hisp.dhis.external.conf.ConfigurationPropertyFactoryBean"><constructor-arg value="LDAP_MANAGER_DN" /></bean>  
+  <bean id="ldapManagerPassword" class="org.hisp.dhis.external.conf.ConfigurationPropertyFactoryBean"><constructor-arg value="LDAP_MANAGER_PASSWORD" /></bean>  
+  <bean id="ldapSearchBase" class="org.hisp.dhis.external.conf.ConfigurationPropertyFactoryBean"><constructor-arg value="LDAP_SEARCH_BASE" /></bean>  
+  <bean id="ldapSearchFilter" class="org.hisp.dhis.external.conf.ConfigurationPropertyFactoryBean"><constructor-arg value="LDAP_SEARCH_FILTER" /></bean>
+  
   <!-- OAuth2 -->
   
   <bean id="clientDetailsService" class="org.hisp.dhis.security.oauth2.DefaultClientDetailsService" />

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/user/hibernate/UserCredentials.hbm.xml'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/user/hibernate/UserCredentials.hbm.xml	2015-10-05 08:38:28 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/org/hisp/dhis/user/hibernate/UserCredentials.hbm.xml	2015-11-03 03:22:58 +0000
@@ -20,7 +20,9 @@
 
     <property name="username" column="username" not-null="true" unique="true" />
 
-    <property name="openId" length="512" column="openid" unique="true" />
+    <property name="openId" column="openid" unique="true" type="text" />
+
+    <property name="ldapId" column="ldapid" unique="true" type="text" />
 
     <property name="password" length="60" column="password" not-null="false" />
 

=== modified file 'dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/ConfigurationKey.java'
--- dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/ConfigurationKey.java	2015-11-02 17:34:22 +0000
+++ dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/ConfigurationKey.java	2015-11-03 14:57:20 +0000
@@ -36,7 +36,8 @@
     LDAP_URL( "ldap.url", "ldaps://0:1" ),
     LDAP_MANAGER_DN( "ldap.manager.dn" ),
     LDAP_MANAGER_PASSWORD( "ldap.manager.password" ),
-    LDAP_DN_PATTERNS( "ldap.dn.patterns", "" ),    
+    LDAP_SEARCH_BASE( "ldap.search.base", "" ),
+    LDAP_SEARCH_FILTER( "ldap.search.filter", "(cn={0})" ),
     FILESTORE_PROVIDER( "filestore.provider", "filesystem" ),
     FILE_STORE_CONTAINER( "filestore.container" ),
     FILE_STORE_LOCATION( "filestore.location" ),

=== modified file 'dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/ConfigurationPropertyFactoryBean.java'
--- dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/ConfigurationPropertyFactoryBean.java	2015-10-28 02:23:35 +0000
+++ dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/ConfigurationPropertyFactoryBean.java	2015-11-03 14:57:20 +0000
@@ -28,42 +28,10 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-import java.util.List;
-
-/*
- * Copyright (c) 2004-2015, University of Oslo
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * Neither the name of the HISP project nor the names of its contributors may
- * be used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
 import org.springframework.beans.factory.FactoryBean;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.util.Assert;
 
-import com.google.common.collect.Lists;
-
 /**
  * Factory bean which allows for DHIS configuration property values to be 
  * injected into target beans. 
@@ -89,13 +57,6 @@
         this.key = key;
     }
     
-    private boolean list;
-
-    public void setList( boolean list )
-    {
-        this.list = list;
-    }
-
     // -------------------------------------------------------------------------
     // FactoryBean implementation
     // -------------------------------------------------------------------------
@@ -106,22 +67,13 @@
     {
         Assert.notNull( key, "Configuration key must be specified" );
         
-        String value = configurationProvider.getProperty( key );
-        
-        if ( list )
-        {
-            return value != null ? Lists.newArrayList( value.split( ";" ) ) : value;
-        }
-        else
-        {
-            return value;
-        }
+        return configurationProvider.getProperty( key );
     }
 
     @Override
-    public Class<?> getObjectType()
+    public Class<String> getObjectType()
     {
-        return list ? List.class : String.class;
+        return String.class;
     }
 
     @Override

=== modified file 'dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/DefaultDhisConfigurationProvider.java'
--- dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/DefaultDhisConfigurationProvider.java	2015-11-02 17:34:22 +0000
+++ dhis-2/dhis-support/dhis-support-external/src/main/java/org/hisp/dhis/external/conf/DefaultDhisConfigurationProvider.java	2015-11-03 03:22:58 +0000
@@ -129,10 +129,8 @@
     {
         String ldapUrl = getProperty( ConfigurationKey.LDAP_URL );
         String managerDn = getProperty( ConfigurationKey.LDAP_MANAGER_DN );
-        String dnPatterns = getProperty( ConfigurationKey.LDAP_DN_PATTERNS );
         
         return !( ConfigurationKey.LDAP_URL.getDefaultValue().equals( ldapUrl ) ||
-            ConfigurationKey.LDAP_MANAGER_DN.getDefaultValue().equals( managerDn ) ||
-            ConfigurationKey.LDAP_DN_PATTERNS.getDefaultValue().equals( dnPatterns ) );
+            ldapUrl == null || managerDn == null );
     }
 }

=== modified file 'dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/security.xml'
--- dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/security.xml	2015-10-12 12:41:20 +0000
+++ dhis-2/dhis-web/dhis-web-commons/src/main/resources/META-INF/dhis/security.xml	2015-11-03 03:22:58 +0000
@@ -5,6 +5,7 @@
     http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd";>
 
   <!-- OAuth2 -->
+  
   <bean id="oauthAuthenticationEntryPoint"
     class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
     <property name="realmName" value="dhis2/oauth2" />
@@ -37,7 +38,6 @@
       login-processing-url="/dhis-web-commons-security/login.action" />
 
   </sec:http>
-  <!-- End OAuth2 -->
 
   <bean id="mappedRedirectStrategy" class="org.hisp.dhis.security.MappedRedirectStrategy">
     <property name="redirectMap">

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/java/org/hisp/dhis/user/action/AddUserAction.java'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/java/org/hisp/dhis/user/action/AddUserAction.java	2015-10-08 13:50:01 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/java/org/hisp/dhis/user/action/AddUserAction.java	2015-11-03 03:22:58 +0000
@@ -183,6 +183,13 @@
     {
         this.openId = openId;
     }
+    
+    private String ldapId;
+
+    public void setLdapId( String ldapId )
+    {
+        this.ldapId = ldapId;
+    }
 
     private String inviteEmail;
 
@@ -289,11 +296,8 @@
         user.setUserCredentials( userCredentials );
 
         userCredentials.setUsername( StringUtils.trimToNull( username ) );
-
-        if ( !StringUtils.isEmpty( openId ) )
-        {
-            userCredentials.setOpenId( openId );
-        }
+        userCredentials.setOpenId( StringUtils.trimToNull( openId ) );
+        userCredentials.setLdapId( StringUtils.trimToNull( ldapId ) );
 
         if ( ACCOUNT_ACTION_INVITE.equals( accountAction ) )
         {

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/java/org/hisp/dhis/user/action/UpdateUserAction.java'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/java/org/hisp/dhis/user/action/UpdateUserAction.java	2015-10-14 07:45:12 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/java/org/hisp/dhis/user/action/UpdateUserAction.java	2015-11-03 03:22:58 +0000
@@ -160,6 +160,13 @@
         this.openId = openId;
     }
 
+    private String ldapId;
+
+    public void setLdapId( String ldapId )
+    {
+        this.ldapId = ldapId;
+    }
+
     private String phoneNumber;
 
     public void setPhoneNumber( String phoneNumber )
@@ -247,14 +254,8 @@
 
         UserCredentials userCredentials = userService.getUserCredentials( user );
 
-        if ( StringUtils.isNotEmpty( openId ) )
-        {
-            userCredentials.setOpenId( StringUtils.trimToNull( openId ) );
-        }
-        else
-        {
-            userCredentials.setOpenId( null );
-        }
+        userCredentials.setOpenId( StringUtils.trimToNull( openId ) );
+        userCredentials.setLdapId( StringUtils.trimToNull( ldapId ) );
 
         if ( jsonAttributeValues != null )
         {

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/resources/org/hisp/dhis/user/i18n_module.properties'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/resources/org/hisp/dhis/user/i18n_module.properties	2015-09-26 16:08:33 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/resources/org/hisp/dhis/user/i18n_module.properties	2015-11-03 03:22:58 +0000
@@ -382,4 +382,5 @@
 replicate=Replicate
 resend_invitation=Resend invitation
 invitation_sent=Invitation sent
-programs = Programs
\ No newline at end of file
+programs = Programs
+ldap_id=LDAP identifier
\ No newline at end of file

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/webapp/dhis-web-maintenance-user/addUserForm.vm'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/webapp/dhis-web-maintenance-user/addUserForm.vm	2015-06-17 13:37:50 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/webapp/dhis-web-maintenance-user/addUserForm.vm	2015-11-03 03:22:58 +0000
@@ -139,6 +139,11 @@
 		<td colspan="3"><input type="text" id="openId" name="openId"></td>
 	</tr>
 
+	<tr class="account">
+		<td><label for="email">$i18n.getString( "ldap_id" )</label></td>
+		<td colspan="3"><input type="text" id="ldapId" name="ldapId"></td>
+	</tr>
+
     <tr class="account">
         <td><label for="phoneNumber">$i18n.getString( "phone_number" )</label></td>
         <td colspan="3"><input type="text" id="phoneNumber" name="phoneNumber"></td>

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/webapp/dhis-web-maintenance-user/updateUserForm.vm'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/webapp/dhis-web-maintenance-user/updateUserForm.vm	2015-10-05 07:55:19 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-user/src/main/webapp/dhis-web-maintenance-user/updateUserForm.vm	2015-11-03 03:22:58 +0000
@@ -126,6 +126,11 @@
     </tr>
 
     <tr>
+      <td><label for="ldapId">$i18n.getString( "ldap_id" )</label></td>
+      <td colspan="3"><input type="text" id="ldapId" name="ldapId" value="$!encoder.htmlEncode( $userCredentials.ldapId )"></td>
+    </tr>
+
+    <tr>
       <td><label for="phoneNumber">$i18n.getString( "phone_number" )</label></td>
       <td colspan="3"><input type="text" id="phoneNumber" name="phoneNumber" value="$!encoder.htmlEncode( $userCredentials.userInfo.phoneNumber )"></td>
     </tr>