001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.console.ldapmanager;
019    
020    import java.util.ArrayList;
021    import java.util.Collection;
022    import java.util.Enumeration;
023    import java.util.HashMap;
024    import java.util.Hashtable;
025    import java.util.List;
026    import java.util.Map;
027    
028    import javax.naming.Context;
029    import javax.naming.NameClassPair;
030    import javax.naming.NamingEnumeration;
031    import javax.naming.NamingException;
032    import javax.naming.directory.Attribute;
033    import javax.naming.directory.Attributes;
034    import javax.naming.directory.DirContext;
035    import javax.naming.directory.InitialDirContext;
036    import javax.naming.directory.SearchControls;
037    import javax.naming.directory.SearchResult;
038    import javax.servlet.http.HttpSession;
039    
040    import org.directwebremoting.annotations.RemoteMethod;
041    import org.directwebremoting.annotations.RemoteProxy;
042    
043    import uk.ltd.getahead.dwr.WebContext;
044    import uk.ltd.getahead.dwr.WebContextFactory;
045    
046    /**
047     * The LDAP manager helper
048     */
049    @RemoteProxy(name="LDAPHelper")
050    public class LDAPManagerHelper {
051        private final static String LDAP_VERSION_KEY = "java.naming.ldap.version";
052    
053        private final static String SSL_VALUE = "ssl";
054    
055        private final static String NONE_VALUE = "none";
056    
057        private final static String INITIAL_CONTEXT_FACTORY_DEFAULT = "com.sun.jndi.ldap.LdapCtxFactory";
058    
059        private final static String HOST_DEFAULT = "localhost";
060    
061        private final static String PORT_DEFAULT = "1389";
062    
063        private final static String BASE_DN_DEFAULT = "ou=system";
064    
065        // LDAP Version: "3", "2"
066        private final static String LDAP_VERSION_DEFAULT = "3";
067    
068        // Security Protocol: "simple", "ssl", "sasl"
069        private final static String SECURITY_PROTOCOL_DEFAULT = "simple";
070    
071        // Security Authentication: "simple", "none", "strong"
072        private final static String SECURITY_AUTHENTICATION_DEFAULT = "simple";
073    
074        private final static String SECURITY_PRINCIPAL_DEFAULT = "uid=admin, ou=system";
075    
076        private final static String SECURITY_CREDENTIALS_DEFAULT = "secret";
077    
078        private final static String ONELEVEL_SCOPE = "onelevel";
079    
080        private final static String SUBTREE_SCOPE = "subtree";
081    
082        private final static String DIR_CONTEXT_KEY = "LDAPManagerHelper.dirContext";
083    
084        private final static String DIR_ENV_KEY = "LDAPManagerHelper.dirEnv";
085    
086        private final static String HOST_KEY = "LDAPManagerHelper.host";
087    
088        private final static String PORT_KEY = "LDAPManagerHelper.port";
089    
090        private final static String BASE_DN_KEY = "LDAPManagerHelper.baseDN";
091    
092        private final static String SUCCESS_RESULT = "<SUCCESS>";
093    
094        private DirContext dirContext;
095    
096        private Hashtable dirEnv;
097    
098        private String host;
099    
100        private String port;
101    
102        private String baseDN;
103    
104        /**
105         * Construct an LDAP manager helper using config data (default)
106         */
107        public LDAPManagerHelper() throws Exception {
108            dirContext = (DirContext) getSessionAttribute(DIR_CONTEXT_KEY);
109            if (dirContext != null) {
110                dirEnv = (Hashtable) getSessionAttribute(DIR_ENV_KEY);
111                host = (String) getSessionAttribute(HOST_KEY);
112                port = (String) getSessionAttribute(PORT_KEY);
113                baseDN = (String) getSessionAttribute(BASE_DN_KEY);
114            }
115        }
116    
117        /**
118         * Construct an LDAP manager helper using config data (partial)
119         */
120        public LDAPManagerHelper(String host, String port, String baseDN,
121                String securityAuthentication, String userDN, String userPwd)
122                throws Exception {
123            connect(INITIAL_CONTEXT_FACTORY_DEFAULT, host, port, baseDN,
124                    LDAP_VERSION_DEFAULT, SECURITY_PROTOCOL_DEFAULT,
125                    securityAuthentication, userDN, userPwd);
126        }
127    
128        /**
129         * Construct an LDAP manager helper using config data (all)
130         */
131        public LDAPManagerHelper(String initialContextFactory, String host,
132                String port, String baseDN, String ldapVersion,
133                String securityProtocol, String securityAuthentication,
134                String securityPrincipal, String securityCredentials)
135                throws Exception {
136            connect(initialContextFactory, host, port, baseDN, ldapVersion,
137                    securityProtocol, securityAuthentication, securityPrincipal,
138                    securityCredentials);
139        }
140    
141        /**
142         * Create a directory context using config data
143         */
144        @RemoteMethod
145        public synchronized String connect(String initialContextFactory,
146                String host, String port, String baseDN, String ldapVersion,
147                String securityProtocol, String securityAuthentication,
148                String securityPrincipal, String securityCredentials)
149                throws Exception {
150            String result = SUCCESS_RESULT;
151    
152            Hashtable dirEnv = new Hashtable();
153            dirEnv.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
154            String providerURL = createLDAPURL(host, port, ""); // Empty Base DN
155            dirEnv.put(Context.PROVIDER_URL, providerURL);
156            dirEnv.put(LDAP_VERSION_KEY, ldapVersion);
157            if (SSL_VALUE.equalsIgnoreCase(securityProtocol)) {
158                dirEnv.put(Context.SECURITY_PROTOCOL, SSL_VALUE);
159            }
160            dirEnv.put(Context.SECURITY_AUTHENTICATION, securityAuthentication);
161            if (!(NONE_VALUE.equalsIgnoreCase(securityAuthentication))) {
162                // Either "simple" or "strong"
163                dirEnv.put(Context.SECURITY_PRINCIPAL, securityPrincipal); // User DN
164                dirEnv.put(Context.SECURITY_CREDENTIALS, securityCredentials); // Password
165            }
166    
167            try {
168                DirContext newDirContext = new InitialDirContext(dirEnv);
169                // Close old context
170                if (dirContext != null) {
171                    dirContext.close();
172                }
173                // Save directory data to class vars
174                this.dirContext = newDirContext;
175                this.dirEnv = dirEnv;
176                this.host = host;
177                this.port = port;
178                this.baseDN = baseDN;
179                // Save directory data to session
180                setSessionAttribute(DIR_CONTEXT_KEY, dirContext);
181                setSessionAttribute(DIR_ENV_KEY, dirEnv);
182                setSessionAttribute(HOST_KEY, host);
183                setSessionAttribute(PORT_KEY, port);
184                setSessionAttribute(BASE_DN_KEY, baseDN);
185            } catch (NamingException e) {
186                result = "Problem connecting to directory server: "
187                        + e.getMessage();
188            }
189    
190            return result;
191        }
192    
193        /**
194         * Return directory context environment
195         */
196        @RemoteMethod
197        public Map getEnvironment() {
198            Map env = new HashMap();
199            env.put("host", host);
200            env.put("port", port);
201            String ldapVersion = (String) dirEnv.get(LDAP_VERSION_KEY);
202            env.put("ldapVersion", ldapVersion);
203            env.put("baseDN", baseDN);
204            String securityProtocol = (String) dirEnv
205                    .get(Context.SECURITY_PROTOCOL);
206            env.put("securityProtocol", securityProtocol);
207            String securityAuthentication = (String) dirEnv
208                    .get(Context.SECURITY_AUTHENTICATION);
209            env.put("securityAuthentication", securityAuthentication);
210            String securityPrincipal = (String) dirEnv
211                    .get(Context.SECURITY_PRINCIPAL);
212            env.put("securityPrincipal", securityPrincipal);
213    
214            return env;
215        }
216    
217        /**
218         * Returns the names bound in the named context
219         */
220        @RemoteMethod
221        public Collection list(String name) throws Exception {
222            ArrayList result = new ArrayList();
223    
224            if (dirContext == null) {
225                return result;
226            }
227    
228            try {
229                NamingEnumeration list = dirContext.list(name); // can't be ""
230    
231                while (list.hasMore()) {
232                    NameClassPair ncp = (NameClassPair) list.next();
233                    String childName = ncp.getName();
234                    String dn = childName + ", " + name;
235                    String[] pair = { childName, dn };
236                    result.add(pair);
237                }
238            } catch (NamingException e) {
239                throw new Exception("Problem getting directory list: "
240                        + e.getMessage());
241            }
242    
243            return result;
244        }
245    
246        /**
247         * Returns the names bound in the base DN context
248         */
249        public Collection listBaseDN() throws Exception {
250            return list(baseDN);
251        }
252    
253        /**
254         * Enumerates the names bound in the named context and return result as JSON
255         */
256        public String listJSON(String name) throws Exception {
257            return listJSON(name, null);
258        }
259    
260        /**
261         * Enumerates the names bound in the named context and return result as JSON
262         */
263        public String listJSON(String name, String commonFields) throws Exception {
264            // JSON: [{title:"Title1",isFolder:true}, {title:"Title2"}]
265    
266            StringBuffer json = new StringBuffer();
267            List list = (List) list(name);
268    
269            json.append('[');
270            int size = list.size();
271            for (int i = 0; i < size; i++) {
272                String[] entry = (String[]) list.get(i);
273                json.append("{title:\"");
274                json.append(entry[0]);
275                json.append("\",widgetId:\"");
276                json.append(entry[1]);
277                json.append("\"");
278                if (commonFields != null) { // TODO: Do additional testing
279                    json.append(commonFields);
280                }
281                json.append("}");
282                if ((i + 1) < size) {
283                    json.append(',');
284                }
285            }
286            json.append("]");
287    
288            return json.toString();
289        }
290    
291        /**
292         * Return the attributes of an LDAP entry
293         */
294        @RemoteMethod
295        public Collection getAttributes(String name) throws Exception {
296            ArrayList result = new ArrayList();
297            
298            if (dirContext == null) {
299                return result;
300            }
301    
302            try {
303                Attributes attribs = dirContext.getAttributes(name);
304                NamingEnumeration attributes = attribs.getAll();
305                while (attributes.hasMore()) {
306                    Attribute attribute = (Attribute) attributes.next();
307                    String id = attribute.getID();
308                    NamingEnumeration values = attribute.getAll();
309                    while (values.hasMore()) {
310                        String value = values.next().toString();
311                        String[] pair = { id, value };
312                        result.add(pair);
313                    }
314                }
315            } catch (NamingException e) {
316                throw new Exception("Problem retrieving attributes: "
317                        + e.getMessage());
318            }
319            return result;
320        }
321    
322        /**
323         * Execute an LDAP search
324         */
325        @RemoteMethod
326        public Collection search(String searchDN, String filter, String searchScope)
327                throws Exception {
328            ArrayList result = new ArrayList();
329    
330            if (dirContext == null) {
331                return result;
332            }
333    
334            try {
335                String ldapURL = createLDAPURL(host, port, searchDN);
336                SearchControls sc = new SearchControls();
337                if (ONELEVEL_SCOPE.equalsIgnoreCase(searchScope)) {
338                    sc.setSearchScope(SearchControls.ONELEVEL_SCOPE);
339                } else if (SUBTREE_SCOPE.equalsIgnoreCase(searchScope)) {
340                    sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
341                } else {
342                    // Default to one level scope
343                    sc.setSearchScope(SearchControls.ONELEVEL_SCOPE);
344                }
345                // Filter: "(objectclass=*)"
346                boolean isSearchDNAdded = false;
347                NamingEnumeration ne = dirContext.search(ldapURL, filter, sc);
348                while (ne.hasMore()) {
349                    SearchResult sr = (SearchResult) ne.next();
350                    String name = sr.getName();
351                    String dn = null;
352                    if (name.length() > 0) {
353                        dn = name + "," + searchDN;
354                        result.add(dn);
355                    } else if ((name.length() == 0) && !isSearchDNAdded) {
356                        dn = searchDN;
357                        result.add(dn);
358                        isSearchDNAdded = true;
359                    }
360                }
361            } catch (NamingException e) {
362                throw new Exception("Problem performing directory search: "
363                        + e.getMessage());
364            }
365            return result;
366        }
367    
368        /**
369         * Close directory context
370         */
371        public void close() throws Exception {
372            if (dirContext != null) {
373                try {
374                    dirContext.close();
375                    dirContext = null;
376                } catch (NamingException e) {
377                    throw new Exception("Problem closing directory context: "
378                                        + e.getMessage());
379                }
380            }
381        }
382    
383        /**
384         * Return base DN of this directory context
385         */
386        @RemoteMethod
387        public String getBaseDN() {
388            return baseDN;
389        }
390    
391        /**
392         * Create an LDAP url using host, port, and base DN
393         */
394        private String createLDAPURL(String host, String port, String baseDN) {
395            StringBuffer url = new StringBuffer();
396            url.append("ldap://");
397            url.append(host);
398            url.append(':');
399            url.append(port);
400            if ((baseDN != null) && (baseDN.length() >= 3)) {
401                if (!baseDN.startsWith("/")) {
402                    url.append('/');
403                }
404                url.append(baseDN);
405            }
406            return url.toString();
407        }
408    
409        /**
410         * Get the HTTP session
411         */
412        private HttpSession getSession() {
413            WebContext ctx = WebContextFactory.get();
414            HttpSession session = ctx.getSession();
415            return session;
416        }
417    
418        /**
419         * Set an HTTP session attribute
420         */
421        private void setSessionAttribute(String name, Object value) {
422            getSession().setAttribute(name, value);
423        }
424    
425        /**
426         * Get an HTTP session attribute
427         */
428        private Object getSessionAttribute(String name) {
429            return getSession().getAttribute(name);
430        }
431    
432        /**
433         * Dump HTTP session attributes
434         */
435        private void dumpSession() {
436            System.out.println("--- dumpSession()");
437            WebContext ctx = WebContextFactory.get();
438            HttpSession session = ctx.getSession();
439            Enumeration attribNames = session.getAttributeNames();
440            while (attribNames.hasMoreElements()) {
441                String attribName = (String) attribNames.nextElement();
442                System.out.print("--- session: " + attribName + " = ");
443                Object attribValue = session.getAttribute(attribName);
444                System.out.println(attribValue);
445            }
446        }
447    
448        /**
449         * Dump search enumeration
450         */
451        private void printSearchEnumeration(NamingEnumeration ne) {
452            try {
453                while (ne.hasMore()) {
454                    SearchResult sr = (SearchResult) ne.next();
455                    System.out.println("-->" + sr.getName());
456                    System.out.println(sr.getAttributes());
457                }
458            } catch (NamingException e) {
459                e.printStackTrace();
460            }
461        }
462    
463    }