1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.services.ldap;
18
19 import java.util.Enumeration;
20 import java.util.Hashtable;
21 import java.util.Properties;
22 import java.util.StringTokenizer;
23 import java.util.Vector;
24 import javax.naming.AuthenticationException;
25 import javax.naming.CommunicationException;
26 import javax.naming.Context;
27 import javax.naming.Name;
28 import javax.naming.NameNotFoundException;
29 import javax.naming.NameParser;
30 import javax.naming.NamingEnumeration;
31 import javax.naming.NamingException;
32 import javax.naming.ReferralException;
33 import javax.naming.directory.Attribute;
34 import javax.naming.directory.Attributes;
35 import javax.naming.directory.DirContext;
36 import javax.naming.directory.InitialDirContext;
37 import javax.naming.directory.ModificationItem;
38 import javax.naming.directory.SearchControls;
39 import javax.naming.directory.SearchResult;
40 import javax.servlet.ServletConfig;
41
42
43 import org.apache.turbine.services.InitializationException;
44 import org.apache.turbine.services.TurbineBaseService;
45 import org.apache.turbine.services.TurbineServices;
46 import org.apache.turbine.services.resources.ResourceService;
47
48
49 import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
50 import org.apache.jetspeed.services.logging.JetspeedLogger;
51
52 /***
53 *
54 * @author <a href="mailto:ender@kilicoglu.nom.tr">Ender KILICOGLU</a>
55 * @author <a href="mailto:sami.leino@netorek.fi">Sami Leino</a>
56 *
57 * @version $Id: LDAPService.java,v 1.6 2004/02/23 03:28:31 jford Exp $
58 *
59 */
60 public class LDAPService extends TurbineBaseService
61 {
62 /***
63 * Static initialization of the logger for this class
64 */
65 private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(LDAPService.class.getName());
66
67 public static String SERVICE_NAME = "ldap";
68 private static final String DEFAULT_ATTR[] = {
69 "objectclass"
70 };
71 public static final int BASE = 0;
72 public static final int ONE = 1;
73 public static final int SUB = 2;
74 public static final int DEFAULT_PORT = 389;
75 public static final int DEFAULT_SSLPORT = 636;
76 public static final int DEFAULT_LIMIT = 0;
77 public static final int DEFAULT_TIMEOUT = 0;
78 public static final int DEFAULT_VERSION = 3;
79 private static String DEFAULT_CTX = "com.sun.jndi.ldap.LdapCtxFactory";
80
81 private Hashtable connections;
82 private Connector connector;
83 private int limit;
84 private int timeout;
85 private int version;
86 private String host;
87 private int port;
88 private int sslport;
89 private String basedn;
90 private String managerdn;
91 private String password;
92 private String managerlogin;
93 private int batchsize;
94 private String securityAuthentication;
95 private String securityProtocol;
96 private String socketFactory;
97 private String saslclientpckgs;
98 private String jndiprovider;
99 private boolean anonymousBind;
100 private String listFilter;
101 private String attributesList[];
102 private NameParser parser;
103 private boolean showOpAttributes;
104 private boolean useCachedDirContexts;
105 private Properties env;
106
107 /***
108 * Main Connection Function
109 *
110 * Make first connection and store it in connections.
111 *
112 * @param url <code>LDAPURL</code> which locate server to connect.
113 * @return boolean true if success else false.
114 */
115 private boolean mainConnect(LDAPURL url)
116 {
117 setDefaultEnv();
118 String base = url.getBase();
119 env.put("java.naming.provider.url", base);
120 try
121 {
122 DirContext ctx = new InitialDirContext(env);
123 if (useCachedDirContexts)
124 {
125 connections.put(basedn, ctx);
126 }
127 if(parser == null) parser = ctx.getNameParser("");
128 return true;
129 }
130 catch(NamingException e)
131 {
132 logger.error ("LDAP Service: Failed to connect to " + url.getUrl(), e);
133 }
134 return false;
135 }
136
137 /***
138 * Connection Function
139 *
140 * tries to connect given <code>LDAPURL</code>.
141 *
142 * @param url <code>LDAPURL</code> which locate server to connect.
143 * @return DirContext connection context object.
144 */
145 public DirContext connect(LDAPURL url)
146 {
147
148 String base = url.getBase();
149 DirContext ctx = (DirContext)connections.get(base);
150 if(ctx != null)
151 {
152
153
154 return ctx;
155 }
156 else
157 {
158
159
160 }
161
162 setDefaultEnv();
163 env.put("java.naming.provider.url", base);
164 do
165 {
166 try
167 {
168 ctx = new InitialDirContext(env);
169 if (useCachedDirContexts) connections.put(base, ctx);
170 return ctx;
171 }
172 catch(AuthenticationException e)
173 {
174 logger.error ("LDAP Service: Authentication error: " + base, e);
175 if(connector == null)
176 return null;
177 Properties pr = connector.referralConnection(env, url, anonymousBind);
178 if(pr != null)
179 {
180 env = pr;
181 continue;
182 }
183 }
184 catch(CommunicationException e)
185 {
186 logger.error("LDAP Service: Communication error: " + base, e);
187 if(connector == null)
188 return null;
189 if(connector.connectionFailed(url))
190 {
191 resetConnection(url);
192 continue;
193 }
194 }
195 catch(NamingException e)
196 {
197 logger.error("LDAP Service:Failed to connect to " + base, e);
198 }
199 return ctx;
200 } while(true);
201 }
202
203 /***
204 * Reset Given Connection Function
205 *
206 * tries to connect given <code>LDAPURL</code>.
207 *
208 * @param url <code>LDAPURL</code> which locate server to connect.
209 *
210 */
211 private void resetConnection(LDAPURL url)
212 {
213
214 connections.remove(url.getBase());
215 }
216 /***
217 * Set Default Environment
218 *
219 * Fill properties necessary to connect.
220 *
221 */
222 private void setDefaultEnv()
223 {
224 showOpAttributes = attributesList != null;
225 env.put("java.naming.referral", "ignore");
226 env.put("java.naming.batchsize", String.valueOf(batchsize));
227
228 if(anonymousBind)
229 {
230 env.remove("java.naming.security.principal");
231 env.remove("java.naming.security.credentials");
232 }
233 else
234 {
235 env.put("java.naming.security.principal", managerdn);
236 env.put("java.naming.security.credentials", password);
237 }
238
239 env.put("java.naming.security.authentication", securityAuthentication);
240 if(saslclientpckgs != null)
241 {
242 env.put("javax.security.sasl.client.pkgs", saslclientpckgs);
243 }
244 else
245 {
246 env.remove("javax.security.sasl.client.pkgs");
247 }
248
249 env.put("java.naming.ldap.derefAliases", "never");
250 env.put("java.naming.ldap.deleteRDN", "true" );
251 env.put("java.naming.ldap.version", String.valueOf(version));
252
253 if( securityProtocol != null)
254 {
255 env.put("java.naming.security.protocol", securityProtocol);
256 if(securityProtocol.equalsIgnoreCase("ssl"))
257 {
258 env.put("java.naming.ldap.factory.socket", socketFactory );
259 }
260 }
261 else
262 {
263 env.remove("java.naming.security.protocol");
264 env.remove("java.naming.ldap.factory.socket");
265 }
266
267
268 env.put("java.naming.factory.initial", (Object)(jndiprovider));
269 }
270
271 /***
272 * Disconnection Function
273 *
274 * tries to disconnect all connection.
275 *
276 * @return boolean true if success else false.
277 */
278
279 public boolean disconnect()
280 {
281
282 DirContext ctx = null;
283
284 for(Enumeration enum = connections.elements(); enum.hasMoreElements();)
285 {
286 try
287 {
288 ctx = (DirContext)enum.nextElement();
289 ctx.close();
290 }
291 catch(NamingException e)
292 {
293 logger.error("LDAP Service: Disconnect failed", e);
294 }
295 }
296
297 connections.clear();
298 return true;
299 }
300
301 public boolean checkAndCloseContext(Context context)
302 {
303 try
304 {
305 if (!useCachedDirContexts)
306 {
307 context.close();
308
309 }
310 else
311 {
312
313 }
314 return true;
315 }
316 catch(NamingException e)
317 {
318 logger.error("LDAP Service: closeContext() failed", e);
319 return false;
320 }
321 }
322
323
324 /***
325 * Delete Atrribute Function
326 *
327 * Delete given attribute for given <code>LDAPURL</code>.
328 *
329 * @param url object affected.
330 * @param at Atribute to delete
331 * @return boolean true if success else false.
332 */
333
334 public boolean deleteAttribute(LDAPURL url, Attribute at)
335 {
336 try
337 {
338 ModificationItem mods[] = new ModificationItem[1];
339 mods[0] = new ModificationItem(3, at);
340 return modifyAttribute(url, mods);
341 }
342 catch(NamingException e)
343 {
344 logger.debug("LDAP Service: Failed to delete '" + at.getID() + "' attribute for " + url.getUrl(), e);
345 }
346 return false;
347 }
348
349 /***
350 * Add Attribute Function
351 *
352 * add given attribute to given <code>LDAPURL</code>.
353 *
354 * @param url object affected.
355 * @param at Atribute to add
356 * @return boolean true if success else false.
357 */
358 public boolean addAttribute(LDAPURL url, Attribute at)
359 {
360 try
361 {
362 ModificationItem mods[] = new ModificationItem[1];
363 mods[0] = new ModificationItem(1, at);
364 return modifyAttribute(url, mods);
365 }
366 catch(NamingException e)
367 {
368 logger.debug("LDAP Service: Failed to add '" + at.getID() + "' attribute for " + url.getUrl(), e);
369 }
370 return false;
371 }
372
373 /***
374 * Add entry Function
375 *
376 * tries to add object with given <code>LDAPURL</code> and
377 * with given attributes.
378 *
379 * @param url object to create.
380 * @param at Atributes to add
381 * @return boolean true if success else false.
382 */
383 public boolean addEntry(LDAPURL url, Attributes at)
384 {
385 DirContext ctx = connect(url);
386
387 if(ctx == null)
388 return false;
389 try
390 {
391 ctx.createSubcontext(url.getDN(), at);
392 checkAndCloseContext(ctx);
393 }
394 catch(ReferralException e)
395 {
396 LDAPURL myurl = getReferralUrl(e);
397 return addEntry(myurl, at);
398 }
399 catch(NamingException e)
400 {
401
402 e.printStackTrace();
403
404 logger.error("LDAP Service: Failed to add new entry " + url.getDN(), e);
405 return false;
406 }
407 return true;
408 }
409
410 /***
411 * Query existense of an Object Function
412 *
413 * tries to locate given <code>LDAPURL</code>.
414 *
415 * @param url object affected.
416 * @return boolean true if exist else false.
417 */
418 public boolean exists(LDAPURL url)
419 {
420 DirContext ctx = connect(url);
421 if(ctx == null) return false;
422
423 try
424 {
425 NamingEnumeration results = search(ctx, url.getDN(), "(objectclass=*)", DEFAULT_ATTR, 0, false);
426 checkAndCloseContext(ctx);
427 return true;
428 }
429 catch(NameNotFoundException _ex)
430 {
431 return false;
432 }
433 catch(NamingException _ex)
434 {
435 return false;
436 }
437 }
438
439 /***
440 * Compare Function
441 *
442 * Compare given <code>LDAPURL</code>s.
443 *
444 * @param srcUrl object affected.
445 * @param dstUrl object affected.
446 * @return int 0 same host+DN, 1 same DN,2 child,3 no relation.
447 */
448 public int compare(LDAPURL srcUrl, LDAPURL dstUrl)
449 {
450 if(!srcUrl.sameHosts(dstUrl))
451 return 0;
452 Name src = parse(srcUrl.getDN());
453 Name dst = parse(dstUrl.getDN());
454 if(dst.compareTo(src) == 0)
455 return 1;
456 if(dst.startsWith(src))
457 return 2;
458 Name prefix = src.getPrefix(src.size() - 1);
459 return dst.compareTo(prefix) != 0 ? 0 : 3;
460 }
461
462 /***
463 * Import Function
464 *
465 * Import given <code>LDAPURL</code> to another dn.
466 *
467 * @param url object to import.
468 * @param dn Dn of new object.
469 * @param entry attributes.
470 * @param type 0 addnew, 1 update, 2 sync.
471 * @return int 1 success, 0 unknown type,-1 failure.
472 */
473 public int importEntry(LDAPURL url, String dn, Attributes entry, int type)
474 {
475 boolean rs = false;
476 LDAPURL myurl = new LDAPURL(url.getHost(), url.getPort(), dn);
477 if(type == 0)
478 rs = addEntry(myurl, entry);
479 else
480
481 if(type == 1)
482 rs = updateEntry(myurl, entry);
483 else
484 if(type == 2)
485 rs = synchEntry(myurl, entry);
486 else
487 return 0;
488 return !rs ? -1 : 1;
489 }
490
491 /***
492 * Modify Function
493 *
494 * Modify given <code>LDAPURL</code> with fiven modification items.
495 *
496 * @param url object to modify.
497 * @param mods Modification items.
498 * @exception NamingException
499 * @return boolean true if success else false.
500 */
501 private boolean modifyAttribute(LDAPURL url, ModificationItem mods[])
502 throws NamingException
503 {
504 DirContext ctx = connect(url);
505 if(ctx == null) return false;
506
507 try
508 {
509 ctx.modifyAttributes(url.getDN(), mods);
510 checkAndCloseContext(ctx);
511 }
512 catch(ReferralException e)
513 {
514 LDAPURL myurl = getReferralUrl(e);
515 return modifyAttribute(myurl, mods);
516 }
517 return true;
518 }
519
520 /***
521 * Build LDAPURL Function
522 *
523 * Build <code>LDAPURL</code> with given DN.
524 *
525 * @param DN DN value for object.
526 * @return LDAPURL build with given DN.
527 */
528 public LDAPURL buildURL(String DN)
529 {
530 return new LDAPURL(host,port,DN + "," + basedn);
531 }
532
533 /***
534 * Read Attributes Function
535 *
536 * Return attributes for given <code>LDAPURL</code>.
537 *
538 * @param url object to read attributes.
539 * @return Attributes attributes for given url.
540 */
541 public Attributes read(LDAPURL url)
542 {
543 DirContext ctx = connect(url);
544 if(ctx == null) return null;
545
546 Attributes attrs = null;
547 try
548 {
549 if(showOpAttributes)
550 {
551 attrs = ctx.getAttributes(url.getDN(), attributesList);
552 }
553 else
554 {
555 attrs = ctx.getAttributes(url.getDN());
556 }
557 checkAndCloseContext(ctx);
558 }
559 catch(ReferralException e)
560 {
561 LDAPURL myurl = getReferralUrl(e);
562 if(myurl.getDN().length() == 0)
563 {
564 myurl.setDN(url.getDN());
565 }
566 return read(myurl);
567 }
568 catch(CommunicationException e)
569 {
570 if(connector == null)
571 {
572 logger.debug("LDAP Service: Communication error : " + url.getBase(), e);
573 return null;
574 }
575 if(connector.connectionFailed(url))
576 {
577 resetConnection(url);
578 }
579 }
580 catch(NamingException e)
581 {
582 logger.debug("LDAP Service: Failed to read entry " + url.getDN(), e);
583 return null;
584 }
585 return attrs;
586 }
587
588 /***
589 * Rename Entry Function
590 *
591 * Rename given <code>LDAPURL</code> with given DN.
592 *
593 * @param url object to modify.
594 * @param newDN DN value for new object.
595 * @return boolean true if success else false.
596 */
597 public boolean renameEntry(LDAPURL url, String newDN)
598 {
599 DirContext ctx = connect(url);
600 if(ctx == null) return false;
601
602 try
603 {
604 ctx.rename(url.getDN(), newDN);
605 checkAndCloseContext(ctx);
606 }
607 catch(ReferralException e)
608 {
609 logger.debug("LDAP Service: Failed to rename entry. (not supported for referrals)", e);
610 return false;
611 }
612 catch(NamingException e)
613 {
614 logger.debug("LDAP Service: Failed to rename entry " + url.getDN(), e);
615 return false;
616 }
617 return true;
618 }
619
620 /***
621 * Sync Entry Function
622 *
623 * Sync given <code>LDAPURL</code> with given atrributes.
624 *
625 * @param url object to sync.
626 * @param ats Modification items.
627 * @return boolean true if success else false.
628 */
629 public boolean synchEntry(LDAPURL url, Attributes ats)
630 {
631 DirContext ctx = connect(url);
632 if(ctx == null) return false;
633
634 try
635 {
636 ctx.modifyAttributes(url.getDN(), 2, ats);
637 checkAndCloseContext(ctx);
638 }
639 catch(ReferralException e)
640 {
641 LDAPURL myurl = getReferralUrl(e);
642 return synchEntry(url, ats);
643 }
644 catch(NameNotFoundException _ex)
645 {
646 try
647 {
648 ctx.createSubcontext(url.getDN(), ats);
649 }
650 catch(NamingException _ex2)
651 {
652 return false;
653 }
654 }
655 catch(NamingException e)
656 {
657 logger.debug("LDAP Service: Failed to synchronize entries", e);
658 return false;
659 }
660 return true;
661 }
662
663 /***
664 * Delete Attributes Function
665 *
666 * Delete Attributes for given <code>LDAPURL</code>.
667 *
668 * @param url object to modify.
669 * @param ats Attributes to delete.
670 * @return boolean true if success else false.
671 */
672 public boolean deleteAttrs(LDAPURL url, Attributes ats)
673 {
674 DirContext ctx = connect(url);
675 if(ctx == null) return false;
676
677 try
678 {
679 ctx.modifyAttributes(url.getDN(), DirContext.REMOVE_ATTRIBUTE, ats);
680 checkAndCloseContext(ctx);
681 }
682 catch(ReferralException e)
683 {
684 LDAPURL myurl = getReferralUrl(e);
685 return synchEntry(url, ats);
686 }
687 catch(NameNotFoundException _ex)
688 {
689 try
690 {
691 ctx.createSubcontext(url.getDN(), ats);
692 checkAndCloseContext(ctx);
693 }
694 catch(NamingException _ex2)
695 {
696 return false;
697 }
698 }
699 catch(NamingException e)
700 {
701 logger.debug("LDAP Service: Failed to delete Attributes", e);
702 return false;
703 }
704 return true;
705 }
706
707 /***
708 * Delete Entry Function
709 *
710 * Delete given <code>LDAPURL</code>.
711 *
712 * @param url object to delete.
713 * @return boolean true if success else false.
714 */
715 public boolean deleteEntry(LDAPURL url)
716 {
717 DirContext ctx = connect(url);
718 if(ctx == null) return false;
719
720 try
721 {
722 ctx.destroySubcontext(url.getDN());
723 checkAndCloseContext(ctx);
724 }
725 catch(ReferralException e)
726 {
727 LDAPURL myurl = getReferralUrl(e);
728 return deleteEntry(myurl);
729 }
730 catch(NamingException e)
731 {
732 logger.debug("LDAP Service: Failed to delete entry " + url.getDN(), e);
733 return false;
734 }
735 return true;
736 }
737
738 /***
739 * Find Entry Name Function
740 *
741 * Return entry name for given <code>LDAPURL</code>.
742 *
743 * @param url object to modify.
744 * @return LDAPURL real entry DN.
745 */
746 public LDAPURL findEntryName(LDAPURL url)
747 {
748 DirContext ctx = connect(url);
749 if(ctx == null) return null;
750
751 Name name = parse(url.getDN());
752 String base = name.getPrefix(name.size() - 1).toString();
753 String dn = url.getDN();
754 String rdn = name.get(name.size() - 1).toString();
755 int i = 1;
756 boolean foundName = true;
757
758 while(foundName)
759 {
760 try
761 {
762 NamingEnumeration results = search(ctx, dn, "(objectclass=*)", DEFAULT_ATTR, 0, false);
763 if(i == 1)
764 rdn = rdn + " copy";
765 else
766 if(i == 2)
767 rdn = rdn + " " + i;
768 else
769 if(i >= 3)
770 rdn = rdn.substring(0, rdn.length() - 1) + i;
771 dn = rdn + ", " + base;
772 i++;
773 }
774 catch(NameNotFoundException _ex)
775 {
776 foundName = false;
777 return new LDAPURL(url.getHost(), url.getPort(), dn);
778 }
779 catch(NamingException _ex)
780 {
781 return null;
782 }
783 }
784
785 checkAndCloseContext(ctx);
786
787 return null;
788 }
789
790 /***
791 * Delete Tree Function
792 *
793 * Delete record with all child node <code>LDAPURL</code>.
794 *
795 * @param url object to modify.
796 * @return boolean true if success else false.
797 */
798 public boolean deleteTree(LDAPURL url)
799 {
800 DirContext ctx = connect(url);
801 if(ctx == null) return false;
802
803 String entryDN = null;
804 LDAPURL myurl = null;
805 String baseDN = url.getDN();
806
807 try
808 {
809 for(NamingEnumeration results = search(ctx, baseDN, "(objectclass=*)", DEFAULT_ATTR, 1, false); results.hasMore();)
810 {
811 SearchResult si = (SearchResult)results.next();
812 entryDN = getFixedDN(si.getName(), baseDN);
813 myurl = new LDAPURL(url.getHost(), url.getPort(), entryDN);
814 if(!deleteTree(myurl))
815 {
816 return false;
817 }
818 }
819
820 checkAndCloseContext(ctx);
821 }
822 catch(NamingException e)
823 {
824 logger.debug("LDAP Service: Delete tree failed", e);
825 return false;
826 }
827 return deleteEntry(url);
828 }
829
830 /***
831 * Transfer Function
832 *
833 * Transfer given <code>LDAPURL</code> to other <code>LDAPURL</code>.
834 *
835 * @param fromUrl object to transfer.
836 * @param toUrl target object.
837 * @param delete delete after transfer.
838 * @param replace replace if exist.
839 * @param withChildren transfer with childs.
840 * @return boolean true if success else false.
841 */
842 public boolean transfer(LDAPURL fromUrl, LDAPURL toUrl, boolean delete, boolean replace, boolean withChildren)
843 {
844 LDAPURL dstUrl = toUrl;
845 int rc = compare(fromUrl, toUrl);
846 if(rc == 1)
847 dstUrl = findEntryName(dstUrl);
848 if(withChildren)
849 return transferTreeSub(fromUrl, dstUrl, delete, replace);
850 else
851 return transferEntry(fromUrl, dstUrl, delete, replace);
852
853 }
854
855 /***
856 * Transfer with updates Function
857 *
858 * Transfer updated <code>LDAPURL</code> with given modification items
859 * to other <code>LDAPURL</code>.
860 *
861 * @param fromUrl object to transfer.
862 * @param toUrl target object.
863 * @param delete delete after transfer.
864 * @param replace replace if exist.
865 * @param ats attributes to update.
866 * @return boolean true if success else false.
867 */
868 public boolean transferEntry(LDAPURL fromUrl, Attributes ats, LDAPURL toUrl, boolean delete, boolean replace)
869 {
870 if(delete && !deleteEntry(fromUrl))
871 return false;
872 if(updateEntry(toUrl, ats, replace))
873 return true;
874 if(delete)
875 addEntry(fromUrl, ats);
876 return false;
877 }
878
879 /***
880 * Transfer without updates Function
881 *
882 * Transfer <code>LDAPURL</code> to other <code>LDAPURL</code>.
883 *
884 * @param fromUrl object to transfer.
885 * @param toUrl target object.
886 * @param delete delete after transfer.
887 * @param replace replace if exist.
888 * @return boolean true if success else false.
889 */
890
891 public boolean transferEntry(LDAPURL fromUrl, LDAPURL toUrl, boolean delete, boolean replace)
892 {
893 Attributes ats = read(fromUrl);
894 if(ats == null)
895 return false;
896 else
897 return transferEntry(fromUrl, ats, toUrl, delete, replace);
898 }
899
900 /***
901 * Transfer Tree Function
902 *
903 * Transfer <code>LDAPURL</code> with all child to other <code>LDAPURL</code>.
904 *
905 * @param fromUrl object to transfer.
906 * @param toUrl target object.
907 * @param delete delete after transfer.
908 * @param replace replace if exist.
909 * @return boolean true if success else false.
910 */
911 private boolean transferTreeSub(LDAPURL fromUrl, LDAPURL toUrl, boolean delete, boolean replace)
912 {
913 DirContext ctx = connect(fromUrl);
914 if(ctx == null) return false;
915
916 Attributes ats = read(fromUrl);
917 if(ats == null) return false;
918
919 String srcDN = fromUrl.getDN();
920 String dstDN = toUrl.getDN();
921 boolean createdBase = false;
922 boolean rc = false;
923 boolean moreReferrals = true;
924
925 while(moreReferrals)
926 {
927 try
928 {
929 NamingEnumeration results = search(ctx, srcDN, "(objectclass=*)", DEFAULT_ATTR, 1, false);
930 if(!results.hasMore())
931 {
932 if(!transferEntry(fromUrl, ats, toUrl, delete, replace))
933 return false;
934 } else
935 {
936 String name = null;
937 if(!createdBase)
938 {
939 if(!updateEntry(toUrl, ats, replace))
940 return false;
941 createdBase = true;
942 }
943 LDAPURL srcUrl;
944 LDAPURL dstUrl;
945 for(; results.hasMore(); transferTreeSub(srcUrl, dstUrl, delete, replace))
946 {
947 SearchResult si = (SearchResult)results.next();
948 name = fixName(si.getName());
949 String tmpSrcDN = getDN(name, srcDN);
950 srcUrl = new LDAPURL(fromUrl.getHost(), fromUrl.getPort(), tmpSrcDN);
951 String tmpDstDN = getDN(name, dstDN);
952 dstUrl = new LDAPURL(toUrl.getHost(), toUrl.getPort(), tmpDstDN);
953 }
954
955 if(delete && !deleteEntry(fromUrl))
956 return false;
957 }
958 moreReferrals = false;
959 }
960 catch(ReferralException e)
961 {
962 if(delete)
963 {
964 moreReferrals = false;
965 }
966 else
967 {
968 if(!createdBase)
969 {
970 if(!updateEntry(toUrl, ats, replace)) return false;
971 createdBase = true;
972 }
973
974 LDAPURL srcUrl = getReferralUrl(e);
975 String tmpDstDN = getName(srcUrl.getDN()) + ", " + dstDN;
976 LDAPURL dstUrl = new LDAPURL(toUrl.getHost(), toUrl.getPort(), tmpDstDN);
977 boolean rs = transferTreeSub(srcUrl, dstUrl, delete, replace);
978 if(!rs)return false;
979
980 moreReferrals = e.skipReferral();
981 try
982 {
983
984 checkAndCloseContext(ctx);
985 ctx = (DirContext)e.getReferralContext();
986 }
987 catch(NamingException _ex) { }
988 }
989 }
990 catch(NamingException e)
991 {
992 logger.debug("LDAP Service: Transfer Tree failed", e);
993 return false;
994 }
995 }
996
997 checkAndCloseContext(ctx);
998 return true;
999 }
1000
1001 /***
1002 * Update Atribute Function
1003 *
1004 * Update an attribute for given <code>LDAPURL</code>.
1005 *
1006 * @param url object to update.
1007 * @param at atrribute to update.
1008 * @return boolean true if success else false.
1009 */
1010 public boolean updateAttribute(LDAPURL url, Attribute at)
1011 {
1012 try
1013 {
1014 ModificationItem mods[] = new ModificationItem[1];
1015 mods[0] = new ModificationItem(2, at);
1016 return modifyAttribute(url, mods);
1017 }
1018 catch(NamingException e)
1019 {
1020 logger.debug("LDAP Service: Failed to update '" + at.getID() + "' attribute for " + url.getUrl(), e);
1021 }
1022 return false;
1023 }
1024
1025 /***
1026 * Update Atributes Function
1027 *
1028 * Update attributes for given <code>LDAPURL</code>.
1029 *
1030 * @param url object to update.
1031 * @param at atrributes to update.
1032 * @return boolean true if success else false.
1033 */
1034 public boolean updateEntry(LDAPURL url, Attributes at)
1035 {
1036 DirContext ctx = connect(url);
1037 if(ctx == null) return false;
1038
1039 try
1040 {
1041 ctx.modifyAttributes(url.getDN(), 2, at);
1042 checkAndCloseContext(ctx);
1043 }
1044 catch(ReferralException e)
1045 {
1046 LDAPURL myurl = getReferralUrl(e);
1047 return updateEntry(myurl, at);
1048 }
1049 catch(NamingException e)
1050 {
1051 logger.error("LDAP Service: Failed to update entry " + url.getDN(), e);
1052 return false;
1053 }
1054 return true;
1055 }
1056
1057 /***
1058 * Update Entry Function
1059 *
1060 * Update attributes for given <code>LDAPURL</code>.
1061 *
1062 * @param url object to update.
1063 * @param ats atrributes to update.
1064 * @param replace replace if exist.
1065 * @return boolean true if success else false.
1066 */
1067 public boolean updateEntry(LDAPURL url, Attributes ats, boolean replace)
1068 {
1069 return replace ? synchEntry(url, ats) : addEntry(url, ats);
1070 }
1071
1072 /***
1073 * Search Function
1074 *
1075 * Search objects for given Base DN and filter.
1076 *
1077 * @param ctx directory context.
1078 * @param dn Base search DN.
1079 * @param filter Search filter.
1080 * @param attribs attributes to receive.
1081 * @param type search scope 1 Subscope, else 0.
1082 * @exception NamingException
1083 * @return NamingEnumeration Results.
1084 */
1085 public NamingEnumeration search(DirContext ctx, String dn, String filter, String attribs[], int type)
1086 throws NamingException
1087 {
1088 return search(ctx, dn, filter, attribs, type, true);
1089 }
1090
1091 /***
1092 * Search Function
1093 *
1094 * Search objects for given Base DN and filter.
1095 *
1096 * @param ctx directory context.
1097 * @param dn Base search DN.
1098 * @param filter Search filter.
1099 * @param attribs attributes to receive.
1100 * @param type search scope 2 Subscope, else 1.
1101 * @param setLimits enable limits.
1102 * @exception NamingException
1103 * @return NamingEnumeration Results.
1104 */
1105 private NamingEnumeration search(DirContext ctx, String dn, String filter, String attribs[], int type, boolean setLimits)
1106 throws NamingException
1107 {
1108 SearchControls constraints = new SearchControls();
1109 constraints.setSearchScope(type);
1110 constraints.setReturningAttributes(attribs);
1111 if(setLimits)
1112 {
1113 constraints.setCountLimit(limit);
1114 constraints.setTimeLimit(timeout);
1115 }
1116 NamingEnumeration results = ctx.search(dn, filter, constraints);
1117 return results;
1118 }
1119
1120 /***
1121 * Search Function
1122 *
1123 * Search objects for given BaseURL and filter.
1124 *
1125 * @param url Base URL .
1126 * @param filter Search filter.
1127 * @param attribs attributes to receive.
1128 * @param subTreeScope true subtree else false.
1129 * @return Vector Results.
1130 */
1131 public Vector search(LDAPURL url, String filter, String attribs[], boolean subTreeScope)
1132 {
1133
1134
1135
1136
1137
1138
1139
1140
1141 Vector results = new Vector();
1142 String attrs[] = new String[attribs.length + 1];
1143 attrs[0] = "objectclass";
1144 System.arraycopy(attribs, 0, attrs, 1, attribs.length);
1145 int scope = subTreeScope ? 2 : 1;
1146 subSearch(url, filter, attrs, scope, results);
1147
1148 return results;
1149 }
1150
1151 /***
1152 * Search Function
1153 *
1154 * Search objects for given BaseURL and filter.
1155 *
1156 * @param url Base URL .
1157 * @param filter Search filter.
1158 * @param attribs attributes to receive.
1159 * @param scope true subtree else false.
1160 * @param rs Result
1161 * @return boolean true if success else false.
1162 */
1163 private boolean subSearch(LDAPURL url, String filter, String attribs[], int scope, Vector rs)
1164 {
1165 DirContext ctx = connect(url);
1166 if(ctx == null) return false;
1167
1168 String entryDN = null;
1169 Attributes at = null;
1170 Attribute a = null;
1171 LDAPURL myurl = null;
1172 int subscope = 0;
1173 String baseDN = url.getDN();
1174
1175 boolean moreReferrals = true;
1176 while(moreReferrals)
1177 {
1178 try
1179 {
1180 Vector vl;
1181 for(NamingEnumeration results = search(ctx, baseDN, filter, attribs, scope); results.hasMore(); rs.addElement(vl))
1182 {
1183 SearchResult si = (SearchResult)results.next();
1184 vl = new Vector(attribs.length);
1185 entryDN = getFixedDN(si.getName(), baseDN);
1186 myurl = new LDAPURL(url.getHost(), url.getPort(), entryDN);
1187 vl.addElement(myurl);
1188 at = si.getAttributes();
1189 for(int i = 1; i < attribs.length; i++)
1190 {
1191 a = at.get(attribs[i]);
1192 if(a == null)
1193 {
1194 vl.addElement("N/A");
1195 } else
1196 {
1197 Object v = a.get();
1198 if(v instanceof byte[])
1199 vl.addElement(v);
1200 else
1201 vl.addElement(a.get().toString());
1202 }
1203 }
1204 }
1205 moreReferrals = false;
1206 }
1207
1208 catch(ReferralException e)
1209 {
1210 myurl = getReferralUrl(e);
1211 subscope = scope != 1 ? scope : 0;
1212 boolean error = subSearch(myurl, filter, attribs, subscope, rs);
1213 if(!error) return error;
1214
1215 moreReferrals = e.skipReferral();
1216 try
1217 {
1218
1219 checkAndCloseContext(ctx);
1220 ctx = (DirContext)e.getReferralContext();
1221 }
1222 catch(NamingException _ex) { }
1223 }
1224 catch(NamingException e)
1225 {
1226 logger.debug("LDAP Service: Search failed", e);
1227 return false;
1228 }
1229 }
1230
1231 checkAndCloseContext(ctx);
1232 return true;
1233 }
1234
1235 /***
1236 * Get value Function
1237 *
1238 * Return value for attribute value pair.
1239 *
1240 * @param attrvalue input.
1241 * @return String Value.
1242 */
1243 public String removeAttrName(String attrvalue)
1244 {
1245 StringTokenizer token = new StringTokenizer(attrvalue,"=");
1246 if (token.countTokens()==2)
1247 {
1248 token.nextToken();
1249 return token.nextToken();
1250 }
1251 else
1252 {
1253 return attrvalue;
1254 }
1255 }
1256
1257 /***
1258 * Return full DN Function
1259 *
1260 * Add Base DN to given DN.
1261 *
1262 * @param rdn full DN.
1263 * @param base Base DN.
1264 * @return String DN.
1265 */
1266 private String getFixedDN(String rdn, String base)
1267 {
1268 return getDN(fixName(rdn), base);
1269 }
1270
1271 /***
1272 * Return Name Function
1273 *
1274 * Return name for given DN.
1275 *
1276 * @param dn DN.
1277 * @return String Name.
1278 */
1279 public String getName(String dn)
1280 {
1281 try
1282 {
1283 Name nm = parser.parse(dn);
1284 return nm.get(nm.size() - 1).toString();
1285 }
1286 catch(NamingException _ex)
1287 {
1288 return null;
1289 }
1290 }
1291
1292 /***
1293 * Fix Name Function
1294 *
1295 * Fix chars .
1296 *
1297 * @param name Name to fix.
1298 * @return String Fixed name.
1299 */
1300 private String fixName(String name)
1301 {
1302 if(name.length() > 0 && name.charAt(0) == '"')
1303 {
1304 int size = name.length() - 1;
1305 StringBuffer buf = new StringBuffer();
1306 for(int i = 1; i < size; i++)
1307 {
1308 if(name.charAt(i) == '/')
1309 buf.append("//");
1310 buf.append(name.charAt(i));
1311 }
1312
1313 return buf.toString();
1314 }
1315 else
1316 {
1317 return name;
1318 }
1319 }
1320
1321 /***
1322 * Return full DN Function
1323 *
1324 * Add Base DN to given DN.
1325 *
1326 * @param rdn DN.
1327 * @param base Base DN.
1328 * @return String full DN.
1329 */
1330 private String getDN(String rdn, String base)
1331 {
1332 if(rdn.length() == 0)
1333 return base;
1334 if(base.length() == 0)
1335 return rdn;
1336 else
1337 return rdn + ", " + base;
1338 }
1339
1340 /***
1341 * Return Name Function
1342 *
1343 * Add Base DN to given DN.
1344 *
1345 * @param dn full DN.
1346 * @return Name Name for given DN.
1347 */
1348 public Name parse(String dn)
1349 {
1350 try
1351 {
1352 return parser.parse(dn);
1353 }
1354 catch(NamingException _ex)
1355 {
1356 return null;
1357 }
1358 }
1359
1360 /***
1361 * Get Referral URL Function
1362 *
1363 * Return <code>LDAPURL</code> extracted from exception.
1364 *
1365 * @param e Exception to extract.
1366 * @return LDAPURL referrral URL.
1367 */
1368 public LDAPURL getReferralUrl(ReferralException e)
1369 {
1370 String url = (String)e.getReferralInfo();
1371 try
1372 {
1373 return new LDAPURL(url);
1374 }
1375 catch(Exception ex)
1376 {
1377 logger.debug("Invalid url: " + ex.getMessage() + " " + url);
1378 }
1379 return null;
1380 }
1381
1382
1383
1384
1385
1386 /***
1387 * This is the early initialization method called by the
1388 * Turbine <code>Service</code> framework
1389 * @param conf The <code>ServletConfig</code>
1390 * @exception InitializationException if the service fails to initialize
1391 */
1392 public void init( ServletConfig conf ) throws InitializationException
1393 {
1394 connections = new Hashtable();
1395 connector = null;
1396 parser = null;
1397 env = new Properties();
1398 ResourceService serviceConf = ((TurbineServices)TurbineServices.getInstance())
1399 .getResources(SERVICE_NAME);
1400 this.host = serviceConf.getString("host");
1401 this.port = serviceConf.getInt("port",DEFAULT_PORT);
1402 this.sslport = serviceConf.getInt("sslport",DEFAULT_SSLPORT);
1403 this.limit = serviceConf.getInt("limit",DEFAULT_LIMIT);
1404 this.timeout = serviceConf.getInt("timeout",DEFAULT_TIMEOUT);
1405 this.version = serviceConf.getInt("version",DEFAULT_VERSION);
1406 this.listFilter = repair(serviceConf.getString("listfilter","(objectclass=*)"));
1407 this.basedn = repair(serviceConf.getString("basedn"));
1408 this.managerdn = repair(serviceConf.getString("managerdn"));
1409 this.password = serviceConf.getString("password");
1410 this.attributesList = getList(serviceConf.getString("attributeslist")," ");
1411 this.showOpAttributes = serviceConf.getBoolean("showopattributes",false);
1412 this.anonymousBind = serviceConf.getBoolean("anonymousbind",false);
1413 this.securityAuthentication = serviceConf.getString("securityauthentication","simple");
1414 this.securityProtocol = serviceConf.getString("securityprotocol");
1415 this.socketFactory = serviceConf.getString("socketfactory");
1416 this.useCachedDirContexts = serviceConf.getBoolean("contextcache", false);
1417
1418 this.jndiprovider = serviceConf.getString("jndiprovider",DEFAULT_CTX);
1419 this.saslclientpckgs = serviceConf.getString("saslclientpckgs");
1420 mainConnect(new LDAPURL(host,port,basedn));
1421 setInit(true);
1422 }
1423
1424 /***
1425 * This is the late initialization method called by the
1426 * Turbine <code>Service</code> framework
1427 * @param conf The <code>ServletConfig</code>
1428 * @exception InitializationException if the service fails to initialize
1429 */
1430 public void init() throws InitializationException
1431 {
1432 while( !getInit() )
1433 {
1434
1435 try
1436 {
1437 Thread.sleep( 500 );
1438 }
1439 catch (InterruptedException ie )
1440 {
1441 logger.error( ie );
1442 }
1443 }
1444 }
1445
1446 /***
1447 * Repair Given Parameter Function
1448 *
1449 * Repair String read from config.
1450 *
1451 * @param value String to repair.
1452 * @return String Repaired String.
1453 */
1454 private String repair(String value)
1455 {
1456 value = value.replace('/', '=');
1457 value = value.replace('%', ',');
1458 return value;
1459 }
1460
1461 /***
1462 * Tokenizer Wrapper Function
1463 *
1464 * Tokenize given string with given parameter.
1465 *
1466 * @param value String to repair.
1467 * @param separator separator
1468 * @return String Result.
1469 */
1470 private String[] getList(String value, String separator)
1471 {
1472 if(value == null) return null;
1473
1474 StringTokenizer tokens = new StringTokenizer(value, separator);
1475 String at[] = new String[tokens.countTokens()];
1476
1477 for(int i = 0; tokens.hasMoreTokens(); i++)
1478 {
1479 at[i] = tokens.nextToken();
1480 }
1481
1482 return at;
1483 }
1484
1485 }