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