Source code: com/xpn/xwiki/user/impl/LDAP/LDAPAuthServiceImpl.java
1 package com.xpn.xwiki.user.impl.LDAP;
2
3 import com.xpn.xwiki.user.api.XWikiAuthService;
4 import com.xpn.xwiki.user.api.XWikiUser;
5 import com.xpn.xwiki.user.impl.xwiki.*;
6 import com.xpn.xwiki.XWikiContext;
7 import com.xpn.xwiki.XWikiException;
8 import com.xpn.xwiki.XWiki;
9 import com.xpn.xwiki.objects.classes.BaseClass;
10 import com.xpn.xwiki.objects.BaseObject;
11 import com.xpn.xwiki.doc.XWikiDocument;
12 import com.novell.ldap.*;
13 import com.novell.ldap.util.Base64;
14 import org.apache.commons.logging.Log;
15 import org.apache.commons.logging.LogFactory;
16 import org.apache.commons.lang.StringUtils;
17 import org.apache.ecs.xhtml.fieldset;
18 import org.securityfilter.authenticator.Authenticator;
19 import org.securityfilter.config.SecurityConfig;
20 import org.securityfilter.filter.SecurityRequestWrapper;
21 import org.securityfilter.realm.SimplePrincipal;
22
23 import javax.servlet.http.HttpServletRequest;
24 import javax.servlet.http.HttpServletResponse;
25 import java.security.Principal;
26 import java.io.IOException;
27 import java.io.UnsupportedEncodingException;
28 import java.util.*;
29 import java.text.Format;
30 import java.text.MessageFormat;
31
32 /**
33 * Created by IntelliJ IDEA.
34 * User: Alex
35 * Date: 18 avr. 2005
36 * Time: 16:18:50
37 * To change this template use File | Settings | File Templates.
38 */
39 public class LDAPAuthServiceImpl extends XWikiAuthServiceImpl {
40 private static final Log log = LogFactory.getLog(LDAPAuthServiceImpl.class);
41
42 public Principal authenticate(String username, String password, XWikiContext context) throws XWikiException {
43 Principal principal = null;
44
45 if ((username==null)||(username.trim().equals("")))
46 return null;
47
48 if ((password==null)||(password.trim().equals("")))
49 return null;
50
51 String superadmin = <font color=red>"superadmin"font>;
52 if (username.equals(superadmin)) {
53 String superadminpassword = context.getWiki().Param(<font color=red>"xwiki.superadminpassword"font>);
54 if ((superadminpassword!=null)&&(superadminpassword.equals(password))) {
55 principal = new SimplePrincipal(<font color=red>"XWiki.superadmin"font>);
56 return principal;
57 } else {
58 return null;
59 }
60 }
61
62 // If we have the context then we are using direct mode
63 // then we should specify the database
64 // This is needed for virtual mode to work
65 if (context!=null) {
66 String susername = username;
67 int i = username.indexOf(<font color=red>"."font>);
68 if (i!=-1)
69 susername = username.substring(i+1);
70
71 String DN = getLDAP_DN(susername, context);
72
73 if (DN != null && DN.length()!=0)
74 {
75 if (checkDNPassword(DN, susername, password, context))
76 {
77 principal = GetUserPrincipal(susername, context);
78 }
79 }
80 else
81 {
82 HashMap attributes = new HashMap();
83 if (checkUserPassword(susername, password, attributes, context))
84 {
85 principal = GetUserPrincipal(susername, context);
86 if (principal == null && attributes.size() > 0)
87 {
88 CreateUserFromLDAP(susername, attributes, context);
89 principal = GetUserPrincipal(susername, context);
90 }
91 }
92 }
93 }
94 return principal;
95 }
96
97 private void CreateUserFromLDAP(String susername, HashMap attributes, XWikiContext context) throws XWikiException {
98 String ldapFieldMapping = getParam(<font color=red>"ldap_fields_mapping"font>,context);
99 if (ldapFieldMapping != null && ldapFieldMapping.length() > 0)
100 {
101 String[] fields = ldapFieldMapping.split(<font color=red>","font>);
102 BaseClass bclass = context.getWiki().getUserClass(context);
103 BaseObject bobj = new BaseObject();
104 bobj.setClassName(bclass.getName());
105 String name = null;
106 String fullwikiname = null;
107 for(int i = 0; i < fields.length; i++ )
108 {
109 String[] field = fields[i].split(<font color=red>"="font>);
110 if (2 == field.length)
111 {
112 String fieldName = field[0];
113 if (attributes.containsKey(field[1]))
114 {
115 String fieldValue;
116 fieldValue = (String)attributes.get(field[1]);
117 if (fieldName.equals(<font color=red>"name"font>))
118 {
119 name = fieldValue;
120 fullwikiname = <font color=red>"XWiki."font> + name;
121 bobj.setName(fullwikiname);
122 }
123 else
124 {
125 bobj.setStringValue(fieldName, fieldValue);
126 }
127 }
128 }
129 }
130
131 if (name != null && name.length() > 0)
132 {
133 XWikiDocument doc = context.getWiki().getDocument(fullwikiname, context);
134 doc.setParent("");
135 doc.addObject(bclass.getName(), bobj);
136 doc.setContent(<font color=red>"#includeForm(\"XWiki.XWikiUserTemplate\")"font>);
137
138 context.getWiki().ProtectUserPage(context, fullwikiname, <font color=red>"edit"font>, doc);
139
140 context.getWiki().saveDocument(doc, null, context);
141
142 context.getWiki().SetUserDefaultGroup(context, fullwikiname);
143 }
144 }
145 }
146
147 protected Principal GetUserPrincipal(String susername, XWikiContext context) {
148 Principal principal = null;
149
150 // First we check in the local database
151 try {
152 String user = findUser(susername, context);
153 if (user!=null) {
154 principal = new SimplePrincipal(user);
155 }
156 } catch (Exception e) {}
157
158 if (context.isVirtual()) {
159 if (principal==null) {
160 // Then we check in the main database
161 String db = context.getDatabase();
162 try {
163 context.setDatabase(context.getWiki().getDatabase());
164 try {
165 String user = findUser(susername, context);
166 if (user!=null)
167 principal = new SimplePrincipal(context.getDatabase() + <font color=red>":"font> + user);
168 } catch (Exception e) {}
169 } finally {
170 context.setDatabase(db);
171 }
172 }
173 }
174 return principal;
175 }
176
177 public String getLDAP_DN(String susername, XWikiContext context)
178 {
179 String DN=null;
180 if (context!=null) {
181 // First we check in the local database
182 try {
183 String user = findUser(susername, context);
184 if (user!=null && user.length()!=0) {
185 DN = readLDAP_DN(user, context);
186 }
187 } catch (Exception e) {}
188
189 if (context.isVirtual()) {
190 if (DN==null && DN.length()!=0) {
191 // Then we check in the main database
192 String db = context.getDatabase();
193 try {
194 context.setDatabase(context.getWiki().getDatabase());
195 try {
196 String user = findUser(susername, context);
197 if (user!=null && user.length()!=0)
198 DN = readLDAP_DN(user, context);
199 } catch (Exception e) {}
200 } finally {
201 context.setDatabase(db);
202 }
203 }
204 }
205 }
206 return DN;
207 }
208
209 private String readLDAP_DN(String username, XWikiContext context) {
210 String DN = null;
211 try {
212 XWikiDocument doc = context.getWiki().getDocument(username, context);
213 // We only allow empty password from users having a XWikiUsers object.
214 if (doc.getObject(<font color=red>"XWiki.XWikiUsers"font>)!=null) {
215 DN = doc.getStringValue(<font color=red>"XWiki.XWikiUsers"font>, <font color=red>"ldap_dn"font>);
216 }
217
218 } catch (Throwable e) {}
219 return DN;
220 }
221
222 protected boolean checkUserPassword(String username, String password, HashMap attributes, XWikiContext context) throws XWikiException {
223 LDAPConnection lc = new LDAPConnection();
224 boolean result = false;
225 boolean notinLDAP = false;
226 String foundDN = null;
227
228 try {
229
230 int ldapPort = getLDAPPort(context);
231 int ldapVersion = LDAPConnection.LDAP_V3;
232 String ldapHost = getParam(<font color=red>"ldap_server"font>, context);
233 String bindDNFormat = getParam(<font color=red>"ldap_bind_DN"font>,context);
234 String bindPasswordFormat = getParam(<font color=red>"ldap_bind_pass"font>,context);
235
236 int checkLevel = GetCheckLevel(context);
237
238 Object[] arguments = {
239 username,
240 password
241 };
242 String bindDN = MessageFormat.format(bindDNFormat, arguments);
243 String bindPassword = MessageFormat.format(bindPasswordFormat, arguments);
244
245 String baseDN = getParam(<font color=red>"ldap_base_DN"font>,context);
246
247
248 lc.connect( ldapHost, ldapPort );
249
250 // authenticate to the server
251 result = Bind(bindDN, bindPassword, lc, ldapVersion);
252
253 if (result && checkLevel > 0)
254 {
255 LDAPSearchResults searchResults =
256 lc.search( baseDN,
257 LDAPConnection.SCOPE_SUB ,
258 <font color=red>"("font>+ getParam(<font color=red>"ldap_UID_attr"font>,context) +
259 <font color=red>"="font> + username + <font color=red>")"font>,
260 null, // return all attributes
261 false); // return attrs and values
262
263 if (searchResults.hasMore())
264 {
265 LDAPEntry nextEntry = searchResults.next();
266
267 foundDN = nextEntry.getDN();
268
269 if (checkLevel > 1)
270 {
271 LDAPAttribute attr = new LDAPAttribute(
272 <font color=red>"userPassword"font>, password );
273 result = lc.compare( foundDN, attr );
274 }
275 if (result)
276 {
277 LDAPAttributeSet attributeSet = nextEntry.getAttributeSet();
278 Iterator allAttributes = attributeSet.iterator();
279
280 while(allAttributes.hasNext()) {
281 LDAPAttribute attribute =
282 (LDAPAttribute)allAttributes.next();
283 String attributeName = attribute.getName();
284
285 Enumeration allValues = attribute.getStringValues();
286
287 if( allValues != null) {
288 while(allValues.hasMoreElements()) {
289 String Value = (String) allValues.nextElement();
290 attributes.put(attributeName, Value);
291 }
292 }
293 }
294 attributes.put(<font color=red>"dn"font>, foundDN);
295 }
296 }
297 else
298 notinLDAP = true;
299
300 if (log.isDebugEnabled()) {
301 if (result)
302 log.debug(<font color=red>"(debug) Password check for user "font> + username + <font color=red>" successfull"font>);
303 else
304 log.debug(<font color=red>"(debug) Password check for user "font> + username + <font color=red>" failed"font>);
305 }
306 }
307 }
308 catch( LDAPException e ) {
309 if ( e.getResultCode() == LDAPException.NO_SUCH_OBJECT ) {
310 notinLDAP = true;
311 } else if ( e.getResultCode() ==
312 LDAPException.NO_SUCH_ATTRIBUTE ) {
313 notinLDAP = true;
314 }
315 }
316 catch (Throwable e) {
317 e.printStackTrace();
318 }
319 finally
320 {
321 try {
322 lc.disconnect();
323 } catch (LDAPException e) {
324 e.printStackTrace();
325 }
326 }
327 if (notinLDAP)
328 {
329 // Use XWiki password if user not in LDAP
330 result = checkPassword(username, password, context);
331 foundDN = null;
332 }
333
334 return result;
335 }
336
337 private String getParam(String name, XWikiContext context) {
338 String param = "";
339 try {
340 param = context.getWiki().getXWikiPreference(name,context);
341 } catch (Exception e) {}
342 if (param == null || "".equals(param))
343 {
344 try{
345 param = context.getWiki().Param(<font color=red>"xwiki.authentication."font> + StringUtils.replace(name, <font color=red>"ldap_"font>,<font color=red>"ldap."font>));
346 } catch (Exception e) {}
347 }
348 if (param == null)
349 param = "";
350 return param;
351 }
352
353 protected int GetCheckLevel(XWikiContext context)
354 {
355 String checkLevel = getParam(<font color=red>"ldap_check_level"font>, context);
356 int val = 2;
357 if (<font color=red>"1"font>.equals(checkLevel))
358 val = 1;
359 else if (<font color=red>"0"font>.equals(checkLevel))
360 val = 0;
361 return val;
362 }
363
364 private int getLDAPPort(XWikiContext context) {
365 try {
366 return context.getWiki().getXWikiPreferenceAsInt(<font color=red>"ldap_port"font>, context);
367 } catch (Exception e) {
368 return (int)context.getWiki().ParamAsLong(<font color=red>"xwiki.authentication.ldap.port"font>, LDAPConnection.DEFAULT_PORT);
369 }
370 }
371
372 protected boolean checkDNPassword(String DN, String username, String password, XWikiContext context) throws XWikiException {
373 LDAPConnection lc = new LDAPConnection();
374 boolean result = false;
375 boolean notinLDAP = false;
376 try {
377
378 int ldapPort = getLDAPPort(context);
379 int ldapVersion = LDAPConnection.LDAP_V3;
380 String ldapHost = getParam(<font color=red>"ldap_server"font>, context);
381 String bindDN = getParam(<font color=red>"ldap_bind_DN"font>,context);
382 String bindPassword = getParam(<font color=red>"ldap_bind_pass"font>,context);
383 String baseDN = getParam(<font color=red>"ldap_base_DN"font>,context);
384
385 lc.connect( ldapHost, ldapPort );
386
387 // authenticate to the server
388 result = Bind(DN, password, lc, ldapVersion);
389
390 if (log.isDebugEnabled()) {
391 if (result)
392 log.debug(<font color=red>"(debug) Password check for user "font> + DN + <font color=red>" successfull"font>);
393 else
394 log.debug(<font color=red>"(debug) Password check for user "font> + DN + <font color=red>" failed"font>);
395 }
396 }
397 catch( LDAPException e ) {
398 if ( e.getResultCode() == LDAPException.NO_SUCH_OBJECT ) {
399 notinLDAP = true;
400 } else if ( e.getResultCode() ==
401 LDAPException.NO_SUCH_ATTRIBUTE ) {
402 notinLDAP = true;
403 }
404 }
405 catch (Throwable e) {
406 e.printStackTrace();
407 }
408 finally
409 {
410 try {
411 lc.disconnect();
412 } catch (LDAPException e) {
413 e.printStackTrace();
414 }
415 }
416 if (notinLDAP)
417 {
418 // Use XWiki password if user not in LDAP
419 result = checkPassword(username, password, context);
420 }
421 return result;
422 }
423
424
425 private boolean Bind(String bindDN, String bindPassword, LDAPConnection lc, int ldapVersion) throws UnsupportedEncodingException {
426 boolean bound = false;
427 if (bindDN != null && bindDN.length() > 0 && bindPassword != null)
428 {
429 try
430 {
431 lc.bind( ldapVersion, bindDN, bindPassword.getBytes(<font color=red>"UTF8"font>) );
432 bound = true;
433 }
434 catch(LDAPException e) { };
435 }
436 return bound;
437 }
438 }