| 81 | |
| 82 | |
| 83 | # Implementation # |
| 84 | |
| 85 | ## Unity IDM LDAP endpoint ## |
| 86 | |
| 87 | * web: http://www.unity-idm.eu/ |
| 88 | * VCS (ldapEndpoint branch): https://www.assembla.com/spaces/unity-public/git/source/ldapEndpoint?type=branch |
| 89 | * documenation: http://www.unity-idm.eu/documentation/unity-1.8.0/manual.html |
| 90 | |
| 91 | ### What do we want? ### |
| 92 | |
| 93 | We want an LDAP endpoint implemented in UNITY so LDAP clients can query UNITY and get user information. |
| 94 | We will *not* implement a 1:1 mapping between LDAP query language and UNITY backend but implement the minimal requirements in |
| 95 | order to support LDAP plugin for Drupal and apache integration. |
| 96 | |
| 97 | ### What do we want in more detail? ### |
| 98 | |
| 99 | We want an LDAP server listening on a port (note that LDAP is not based on http). Because Unity is based on Jetty and servlet containers are in general |
| 100 | unfriendly to non http protocols, an embedded LDAP server is used that listens on a different port than Unity. Moreover, there are only a few |
| 101 | +- working LDAP servers so the choice was easy - Apache DS (https://directory.apache.org/studio/). Furthermore, fiddling directly with LDAP implementation |
| 102 | is to be avoided if possible in favour of sustainability. Therefore, custom interceptors (https://directory.apache.org/apacheds/advanced-ug/1.4-interceptors.html) were used to intercept LDAP API and hook it to Unity. |
| 103 | |
| 104 | ### How to do it? ### |
| 105 | |
| 106 | Unity defines a set of endpoints where we add our LDAPSserver |
| 107 | |
| 108 | {{{ |
| 109 | unityServer.core.endpoints.11.endpointType=LDAPServer |
| 110 | unityServer.core.endpoints.11.endpointConfigurationFile=conf/endpoints/ldap.properties |
| 111 | unityServer.core.endpoints.11.contextPath=/ldap |
| 112 | unityServer.core.endpoints.11.endpointName=Ldap info endpoint |
| 113 | unityServer.core.endpoints.11.endpointRealm=defaultRealm |
| 114 | unityServer.core.endpoints.11.endpointAuthenticators=pwdRaw |
| 115 | }}} |
| 116 | |
| 117 | The server uses a new authenticator `pwdRaw` defined as |
| 118 | |
| 119 | |
| 120 | {{{ |
| 121 | unityServer.core.authenticators.6.authenticatorName=pwdRaw |
| 122 | unityServer.core.authenticators.6.authenticatorType=password with raw-password |
| 123 | unityServer.core.authenticators.6.localCredential=Password credential |
| 124 | unityServer.core.authenticators.6.retrievalConfigurationFile=conf/authenticators/passwordRetrieval.json |
| 125 | }}} |
| 126 | |
| 127 | The authenticator uses `password with raw-password`. Note that this names two classes responsible for the authenticator. Default `password` and our `raw-password`. |
| 128 | |
| 129 | The binding is done dynamically through spring in `components.xml` as follows: |
| 130 | |
| 131 | |
| 132 | {{{ |
| 133 | <!-- LDAP server --> |
| 134 | <bean class="pl.edu.icm.unity.ldap.endpoint.LdapEndpointFactory"/> |
| 135 | <bean class="pl.edu.icm.unity.ldap.endpoint.RawPasswordRetrievalFactory"/> |
| 136 | }}} |
| 137 | |
| 138 | RawPassowrdRetrievalFactory is the `raw-password` in our configuration |
| 139 | |
| 140 | |
| 141 | {{{ |
| 142 | @Component |
| 143 | public class RawPasswordRetrievalFactory implements CredentialRetrievalFactory |
| 144 | { |
| 145 | public static final String NAME = "raw-password"; |
| 146 | |
| 147 | }}} |
| 148 | |
| 149 | |
| 150 | When Unity is started, the important part is done in |
| 151 | |
| 152 | |
| 153 | {{{ |
| 154 | public class LdapEndpointFactory implements EndpointFactory |
| 155 | }}} |
| 156 | |
| 157 | that defines (well, kind of again) the endpoint and later newInstance of this endpoint is called. This instantiates `LdapEndpoint` |
| 158 | of which the `getServletContextHandler` method is called in which we start the embedded LDAP server and |
| 159 | |
| 160 | |
| 161 | {{{ |
| 162 | // |
| 163 | RawPasswordRetrieval rpr = (RawPasswordRetrieval)(authenticators.get(0).getPrimaryAuthenticator()); |
| 164 | // |
| 165 | startLdapEmbeddedServer(rpr); |
| 166 | }}} |
| 167 | |
| 168 | The start of the LDAP server and the functionality is configured from `ldap.properties` file that looks like: |
| 169 | |
| 170 | |
| 171 | {{{ |
| 172 | # leave empty for default server address |
| 173 | unity.ldapServer.host=192.168.2.4 |
| 174 | # default ldap port is 389 |
| 175 | unity.ldapServer.ldap_port=389 |
| 176 | unity.ldapServer.ldaps_port=636 |
| 177 | # hardcoded - unity.endpoint.ldapServer.bind.DN="uid=admin,ou=system" |
| 178 | unity.ldapServer.bind_password=XXX |
| 179 | |
| 180 | unity.ldapServer.group_query=ougroups |
| 181 | unity.ldapServer.user_query=cn |
| 182 | unity.ldapServer.group_member=member |
| 183 | unity.ldapServer.group_member_user_regexp=cn |
| 184 | # return these attributes if the operation requests all user attributes |
| 185 | # e.g., values of SchemaConstants.CN_AT |
| 186 | unity.ldapServer.returned_user_attributes=cn,entryDN,jpegPhoto |
| 187 | }}} |
| 188 | |
| 189 | |
| 190 | The real logic is in `LdapApacheDSInterceptor` that implements interceptors for Apache DS LDAP API. Apache DS is threaded so these methods run in the context of Unity. |
| 191 | |
| 192 | ### Few notes on integration ### |
| 193 | |
| 194 | * Apache - |
| 195 | |
| 196 | |
| 197 | {{{ |
| 198 | // Require ldap-user - cn should be enough |
| 199 | // Require ldap-group - groups |
| 200 | // Require ldap-dn - cn should be enough |
| 201 | // Require ldap-attribute - not supporting |
| 202 | // Require ldap-filter - not supporting |
| 203 | }}} |
| 204 | |
| 205 | * Drupal LDAP modules |
| 206 | |
| 207 | * one module defined and the other did not the attributes that should be returned |
| 208 | |
| 209 | |
| 210 | ### Notes in general ### |
| 211 | |
| 212 | * windows - IntelliJ Idea had to be configured like this (mvn package, added dependencies to other modules, disabled annotations, apacheds-service library (if used) had to be the last dependency) |
| 213 | * apacheds-service library is a mess full of old libraries that conflict in runtime with unity (see above) |
| 214 | * in order to use a new property, you have to define it in `LdapServerProperties` |
| 215 | |
| 216 | |
| 217 | |
| 218 | |
| 219 | |
| 220 | |
| 221 | |
| 222 | |
| 223 | |
| 224 | |
| 225 | |
| 226 | |
| 227 | |
| 228 | |
| 229 | |
| 230 | |
| 231 | |
| 232 | |
| 233 | |
| 234 | |
| 235 | |
| 236 | |
| 237 | |
| 238 | |
| 239 | |
| 240 | |
| 241 | |
| 242 | |
| 243 | |
| 244 | |
| 245 | |