]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/directory/ldap/LdapConnection.java
Improve ACR attribute typing.
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / directory / ldap / LdapConnection.java
1 package org.argeo.cms.directory.ldap;
2
3 import java.util.Dictionary;
4 import java.util.Hashtable;
5
6 import javax.naming.CommunicationException;
7 import javax.naming.Context;
8 import javax.naming.NameNotFoundException;
9 import javax.naming.NamingEnumeration;
10 import javax.naming.NamingException;
11 import javax.naming.directory.Attributes;
12 import javax.naming.directory.DirContext;
13 import javax.naming.directory.SearchControls;
14 import javax.naming.directory.SearchResult;
15 import javax.naming.ldap.InitialLdapContext;
16 import javax.naming.ldap.LdapName;
17
18 import org.argeo.api.acr.ldap.LdapAttr;
19 import org.argeo.api.cms.transaction.WorkingCopy;
20
21 /** A synchronized wrapper for a single {@link InitialLdapContext}. */
22 // TODO implement multiple contexts and connection pooling.
23 public class LdapConnection {
24 private InitialLdapContext initialLdapContext = null;
25
26 public LdapConnection(String url, Dictionary<String, ?> properties) {
27 try {
28 Hashtable<String, Object> connEnv = new Hashtable<String, Object>();
29 connEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
30 connEnv.put(Context.PROVIDER_URL, url);
31 connEnv.put("java.naming.ldap.attributes.binary", LdapAttr.userPassword.name());
32 // use pooling in order to avoid connection timeout
33 // connEnv.put("com.sun.jndi.ldap.connect.pool", "true");
34 // connEnv.put("com.sun.jndi.ldap.connect.pool.timeout", 300000);
35
36 initialLdapContext = new InitialLdapContext(connEnv, null);
37 // StartTlsResponse tls = (StartTlsResponse) ctx
38 // .extendedOperation(new StartTlsRequest());
39 // tls.negotiate();
40 Object securityAuthentication = properties.get(Context.SECURITY_AUTHENTICATION);
41 if (securityAuthentication != null)
42 initialLdapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, securityAuthentication);
43 else
44 initialLdapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
45 Object principal = properties.get(Context.SECURITY_PRINCIPAL);
46 if (principal != null) {
47 initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, principal.toString());
48 Object creds = properties.get(Context.SECURITY_CREDENTIALS);
49 if (creds != null) {
50 initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, creds.toString());
51 }
52 }
53 } catch (NamingException e) {
54 throw new IllegalStateException("Cannot connect to LDAP", e);
55 }
56
57 }
58
59 public void init() {
60
61 }
62
63 public void destroy() {
64 try {
65 // tls.close();
66 initialLdapContext.close();
67 initialLdapContext = null;
68 } catch (NamingException e) {
69 e.printStackTrace();
70 }
71 }
72
73 protected InitialLdapContext getLdapContext() {
74 return initialLdapContext;
75 }
76
77 protected void reconnect() throws NamingException {
78 initialLdapContext.reconnect(initialLdapContext.getConnectControls());
79 }
80
81 public synchronized NamingEnumeration<SearchResult> search(LdapName searchBase, String searchFilter,
82 SearchControls searchControls) throws NamingException {
83 NamingEnumeration<SearchResult> results;
84 try {
85 results = getLdapContext().search(searchBase, searchFilter, searchControls);
86 } catch (CommunicationException e) {
87 reconnect();
88 results = getLdapContext().search(searchBase, searchFilter, searchControls);
89 }
90 return results;
91 }
92
93 public synchronized Attributes getAttributes(LdapName name) throws NamingException {
94 try {
95 return getLdapContext().getAttributes(name);
96 } catch (CommunicationException e) {
97 reconnect();
98 return getLdapContext().getAttributes(name);
99 }
100 }
101
102 public synchronized boolean entryExists(LdapName name) throws NamingException {
103 String[] noAttrOID = new String[] { "1.1" };
104 try {
105 getLdapContext().getAttributes(name, noAttrOID);
106 return true;
107 } catch (CommunicationException e) {
108 reconnect();
109 getLdapContext().getAttributes(name, noAttrOID);
110 return true;
111 } catch (NameNotFoundException e) {
112 return false;
113 }
114 }
115
116 public synchronized void prepareChanges(WorkingCopy<?, ?, LdapName> wc) throws NamingException {
117 // make sure connection will work
118 reconnect();
119
120 // delete
121 for (LdapName dn : wc.getDeletedData().keySet()) {
122 if (!entryExists(dn))
123 throw new IllegalStateException("User to delete no found " + dn);
124 }
125 // add
126 for (LdapName dn : wc.getNewData().keySet()) {
127 if (entryExists(dn))
128 throw new IllegalStateException("User to create found " + dn);
129 }
130 // modify
131 for (LdapName dn : wc.getModifiedData().keySet()) {
132 if (!wc.getNewData().containsKey(dn) && !entryExists(dn))
133 throw new IllegalStateException("User to modify not found " + dn);
134 }
135
136 }
137
138 // protected boolean entryExists(LdapName dn) throws NamingException {
139 // try {
140 // return getAttributes(dn).size() != 0;
141 // } catch (NameNotFoundException e) {
142 // return false;
143 // }
144 // }
145
146 public synchronized void commitChanges(LdapEntryWorkingCopy wc) throws NamingException {
147 // delete
148 for (LdapName dn : wc.getDeletedData().keySet()) {
149 getLdapContext().destroySubcontext(dn);
150 }
151 // add
152 for (LdapName dn : wc.getNewData().keySet()) {
153 LdapEntry user = wc.getNewData().get(dn);
154 getLdapContext().createSubcontext(dn, user.getAttributes());
155 }
156 // modify
157 for (LdapName dn : wc.getModifiedData().keySet()) {
158 Attributes modifiedAttrs = wc.getModifiedData().get(dn);
159 getLdapContext().modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, modifiedAttrs);
160 }
161 }
162 }