001    package railo.runtime.net.ldap;
002    
003    import java.io.IOException;
004    import java.security.Security;
005    import java.util.Enumeration;
006    import java.util.Hashtable;
007    
008    import javax.naming.NamingEnumeration;
009    import javax.naming.NamingException;
010    import javax.naming.directory.Attribute;
011    import javax.naming.directory.Attributes;
012    import javax.naming.directory.BasicAttribute;
013    import javax.naming.directory.BasicAttributes;
014    import javax.naming.directory.DirContext;
015    import javax.naming.directory.InitialDirContext;
016    import javax.naming.directory.ModificationItem;
017    import javax.naming.directory.SearchControls;
018    import javax.naming.directory.SearchResult;
019    import javax.naming.ldap.Control;
020    import javax.naming.ldap.InitialLdapContext;
021    
022    import railo.commons.lang.ClassException;
023    import railo.commons.lang.ClassUtil;
024    import railo.commons.lang.StringUtil;
025    import railo.runtime.exp.PageException;
026    import railo.runtime.op.Caster;
027    import railo.runtime.type.Collection;
028    import railo.runtime.type.KeyImpl;
029    import railo.runtime.type.Query;
030    import railo.runtime.type.QueryImpl;
031    import railo.runtime.type.util.ListUtil;
032    
033    import com.sun.jndi.ldap.ctl.SortControl;
034    import com.sun.jndi.ldap.ctl.SortKey;
035    import com.sun.net.ssl.internal.ssl.Provider;
036    
037    
038    /**
039     * Ldap Client
040     */
041    public final class LDAPClient {
042    
043            /**
044             * Field <code>SECURE_NONE</code>
045             */
046            public static final short SECURE_NONE=0;
047            /**
048             * Field <code>SECURE_CFSSL_BASIC</code>
049             */
050            public static final short SECURE_CFSSL_BASIC=1;
051            /**
052             * Field <code>SECURE_CFSSL_CLIENT_AUTH</code>
053             */
054            public static final short SECURE_CFSSL_CLIENT_AUTH=2;
055            
056            /**
057             * Field <code>SORT_TYPE_CASE</code>
058             */
059            public static final int SORT_TYPE_CASE = 0;
060            /**
061             * Field <code>SORT_TYPE_NOCASE</code>
062             */
063            public static final int SORT_TYPE_NOCASE = 1;
064    
065            /**
066             * Field <code>SORT_DIRECTION_ASC</code>
067             */
068            public static final int SORT_DIRECTION_ASC = 0;
069            /**
070             * Field <code>SORT_DIRECTION_DESC</code>
071             */
072        public static final int SORT_DIRECTION_DESC = 1;
073        
074            Hashtable env=new Hashtable();
075            
076            
077            /**
078             * constructor of the class
079             * @param server
080             * @param port
081             * @param binaryColumns
082             */
083            public LDAPClient(String server, int port,String[] binaryColumns) {
084    
085                            env.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
086                            env.put("java.naming.provider.url", "ldap://" + server+":"+port);
087          
088           // rEAD AS bINARY
089                for(int i = 0; i < binaryColumns.length; i++)env.put("java.naming.ldap.attributes.binary", binaryColumns[i]);
090    
091           // Referral
092                env.put("java.naming.referral", "ignore");
093            }
094        
095        /**
096         * sets username password for the connection
097         * @param username
098         * @param password
099         */
100        public void setCredential(String username, String password) {
101            if(username != null) {
102                env.put("java.naming.security.principal", username);
103                env.put("java.naming.security.credentials", password);
104            } 
105            else {
106                env.remove("java.naming.security.principal");
107                env.remove("java.naming.security.credentials");
108            }
109        }
110        
111        /**
112         * sets the secure Level
113         * @param secureLevel [SECURE_CFSSL_BASIC, SECURE_CFSSL_CLIENT_AUTH, SECURE_NONE]
114         * @throws ClassNotFoundException 
115         * @throws ClassException 
116         */
117        public void setSecureLevel(short secureLevel) throws ClassException {
118            // Security
119            if(secureLevel==SECURE_CFSSL_BASIC) {
120                env.put("java.naming.security.protocol", "ssl");
121                env.put("java.naming.ldap.factory.socket", "javax.net.ssl.SSLSocketFactory");
122                //Class.orName("com.sun.net.ssl.internal.ssl.Provider");
123                ClassUtil.loadClass("com.sun.net.ssl.internal.ssl.Provider");
124                
125                Security.addProvider(new Provider());
126                
127            } 
128            else if(secureLevel==SECURE_CFSSL_CLIENT_AUTH) {
129                env.put("java.naming.security.protocol", "ssl");
130                env.put("java.naming.security.authentication", "EXTERNAL");
131            }
132            else {
133                env.put("java.naming.security.authentication", "simple");
134                env.remove("java.naming.security.protocol");
135                env.remove("java.naming.ldap.factory.socket");
136            } 
137        }
138        
139        /**
140         * sets thr referral
141         * @param referral
142         */
143        public void setReferral(int referral) {
144            if(referral > 0) {
145                env.put("java.naming.referral", "follow");
146                env.put("java.naming.ldap.referral.limit", Caster.toString(referral));
147            } 
148            else {
149                env.put("java.naming.referral", "ignore");
150                env.remove("java.naming.ldap.referral.limit");
151            }
152        }
153        
154    
155    
156            /**
157             * adds LDAP entries to LDAP server
158             * @param dn
159             * @param attributes
160             * @param delimiter 
161             * @throws NamingException 
162             * @throws PageException 
163             */
164            public void add(String dn, String attributes, String delimiter, String seperator) throws NamingException, PageException {
165                    DirContext ctx = new InitialDirContext(env);
166                ctx.createSubcontext(dn, toAttributes(attributes,delimiter,seperator));
167                ctx.close();
168            }
169            
170        /**
171         * deletes LDAP entries on an LDAP server
172         * @param dn
173         * @throws NamingException
174         */
175        public void delete(String dn)  throws NamingException{
176            DirContext ctx = new InitialDirContext(env);
177            ctx.destroySubcontext(dn);
178            ctx.close();
179        }
180        
181        /**
182         *  modifies distinguished name attribute for LDAP entries on LDAP server
183         * @param dn
184         * @param attributes
185         * @throws NamingException 
186         */
187        public void modifydn(String dn, String attributes)  throws NamingException{
188            DirContext ctx = new InitialDirContext(env);
189                ctx.rename(dn, attributes);
190                ctx.close();
191        }
192        
193        public void modify(String dn, int modifytype, String strAttributes, String delimiter, String separator) throws NamingException, PageException {
194    
195                DirContext context = new InitialDirContext(env);
196                String strArrAttributes[] = toStringAttributes(strAttributes,delimiter);
197                
198                int count = 0;
199                for(int i=0; i<strArrAttributes.length; i++) {
200                    String[] attributesValues = getAttributesValues(strArrAttributes[i], separator);
201                    if(attributesValues == null)count++;
202                    else count+=attributesValues.length;
203                }
204                
205                ModificationItem modItems[] = new ModificationItem[count];
206                BasicAttribute basicAttr = null;
207                int k = 0;
208                for(int i = 0; i < strArrAttributes.length; i++) {
209                    String attribute = strArrAttributes[i];
210                    String type = getAttrValueType(attribute);
211                    String values[] = getAttributesValues(attribute,separator);
212                    
213                    if(modifytype==DirContext.REPLACE_ATTRIBUTE) {
214                        if(values == null) basicAttr = new BasicAttribute(type);
215                        else basicAttr = new BasicAttribute(type, values[0]);
216                        
217                        modItems[k] = new ModificationItem(modifytype, basicAttr);
218                        k++;
219                        if(values != null && values.length > 1) {
220                            for(int j = 1; j < values.length; j++) {
221                                basicAttr = new BasicAttribute(type, values[j]);
222                                modItems[k] = new ModificationItem(DirContext.ADD_ATTRIBUTE, basicAttr);
223                                k++;
224                            }
225                        }
226                    } 
227                    else {
228                        for(int j = 0; j < values.length; j++) {
229                            if(type != null || modifytype==DirContext.ADD_ATTRIBUTE)
230                                 basicAttr = new BasicAttribute(type, values[j]);
231                            else basicAttr = new BasicAttribute(values[j]);
232                            modItems[k] = new ModificationItem(modifytype, basicAttr);
233                            k++;
234                        }
235                    }
236                }
237    
238                context.modifyAttributes(dn, modItems);
239                context.close();
240    
241        }
242        
243        
244        
245        /**
246         * @param dn
247         * @param strAttributes
248         * @param scope
249         * @param startrow
250         * @param maxrows
251         * @param timeout
252         * @param sort
253         * @param sortType
254         * @param sortDirection
255         * @param start
256         * @param separator
257         * @param filter
258         * @return
259         * @throws NamingException
260         * @throws PageException
261         * @throws IOException 
262         */
263        public Query query(String strAttributes,int scope, int startrow, int maxrows, int timeout, 
264                String[] sort, int sortType, int sortDirection, 
265                String start, String separator, String filter) 
266            throws NamingException, PageException, IOException {
267            //strAttributes=strAttributes.trim();
268            boolean attEQAsterix=strAttributes.trim().equals("*");
269            String[] attributes = attEQAsterix?new String[]{"name","value"}:toStringAttributes(strAttributes,",");
270            
271            
272            
273            // Control
274            SearchControls controls = new SearchControls();
275            controls.setReturningObjFlag(true);
276            controls.setSearchScope(scope);
277            if(!attEQAsterix)controls.setReturningAttributes(toStringAttributes(strAttributes,","));
278            if(maxrows>0)controls.setCountLimit(startrow + maxrows + 1);
279            if(timeout>0)controls.setTimeLimit(timeout);
280            
281    
282            InitialLdapContext context = new InitialLdapContext(env, null);
283            
284            
285            // Sort
286            if(sort!=null && sort.length>0) {
287                boolean isSortAsc=sortDirection==SORT_DIRECTION_ASC;
288                
289                SortKey keys[] = new SortKey[sort.length];
290                for(int i=0;i<sort.length;i++) {
291                    String item=sort[i].equalsIgnoreCase("dn")?"name":sort[i];
292                    if(item.indexOf(' ')!=-1)item=ListUtil.first(item," ",true);
293                    keys[i] = new SortKey(item,isSortAsc ,sortType==LDAPClient.SORT_TYPE_CASE?null/*"CASE"*/:null);
294                    //keys[i] = new SortKey(item);
295                }
296                context.setRequestControls(new Control[]{new SortControl(keys, Control.CRITICAL)});
297            }        
298            
299            // Search
300            Query qry=new QueryImpl(attributes,0,"query");
301            try {
302                NamingEnumeration results = context.search(start, filter, controls);
303                
304                // Fill result
305                int row=1;
306                if(!attEQAsterix) {
307                    while(results.hasMoreElements()) {
308                        SearchResult resultRow = (SearchResult)results.next();
309                        if(row++<startrow)continue;
310                        
311                        int len=qry.addRow();
312                        NamingEnumeration rowEnum = resultRow.getAttributes().getAll();
313                        String dn = resultRow.getNameInNamespace(); 
314                        qry.setAtEL("dn",len,dn);
315                        while(rowEnum.hasMore()) {
316                            Attribute attr = (Attribute)rowEnum.next();
317                            Collection.Key key = KeyImpl.init(attr.getID());
318                            Enumeration values = attr.getAll();
319                            Object value;
320                            String existing,strValue;
321                            while(values.hasMoreElements()) {
322                                value = values.nextElement();
323                                strValue=Caster.toString(value,null);
324                                existing=Caster.toString(qry.getAt(key, len,null),null);
325                                if(!StringUtil.isEmpty(existing) && !StringUtil.isEmpty(strValue)) {
326                                    value = existing + separator + strValue;
327                                    }
328                                else if(!StringUtil.isEmpty(existing))
329                                    value = existing;
330                                
331                                    qry.setAtEL(key,len,value);
332                            }            
333                        }
334                        if(maxrows>0 && len>=maxrows)break;
335                    }
336                }
337                else {
338                    
339                    outer:while(results.hasMoreElements()) {
340                        SearchResult resultRow = (SearchResult)results.next();
341                        if(row++<startrow)continue;
342                        
343                        Attributes attributesRow = resultRow.getAttributes();
344                        NamingEnumeration rowEnum = attributesRow.getIDs();
345                        while(rowEnum.hasMoreElements()) {
346                            int len=qry.addRow();
347                            String name = Caster.toString(rowEnum.next());
348                            Object value=null;
349                            
350                            try {
351                                value=attributesRow.get(name).get();
352                            }catch(Exception e) {}
353                            
354                            qry.setAtEL("name",len,name);
355                            qry.setAtEL("value",len,value);
356                            if(maxrows>0 && len>=maxrows)break outer;
357                        }
358                        qry.setAtEL("name",qry.size(),"dn");
359                    }
360                }
361            }
362            finally {
363                context.close();
364            }
365            
366            return qry;
367        }
368    
369        private static String[] toStringAttributes(String strAttributes,String delimiter) throws PageException {
370                    return ListUtil.toStringArrayTrim(ListUtil.listToArrayRemoveEmpty(strAttributes,delimiter));            
371            }
372            
373            private static Attributes toAttributes(String strAttributes,String delimiter, String separator) throws PageException {
374                    String[] arrAttr = toStringAttributes(strAttributes,delimiter);
375                    
376                    
377                    BasicAttributes attributes = new BasicAttributes();
378            for(int i=0; i<arrAttr.length; i++) {
379                String strAttr = arrAttr[i];
380                
381                // Type
382                int eqIndex=strAttr.indexOf('=');
383                Attribute attr = new BasicAttribute((eqIndex != -1)?strAttr.substring(0, eqIndex).trim():null);
384                
385                // Value
386                String strValue = (eqIndex!=-1)?strAttr.substring( eqIndex+ 1):strAttr;
387                String[] arrValue=ListUtil.toStringArray(ListUtil.listToArrayRemoveEmpty(strValue,separator));
388                
389                // Fill
390                for(int y=0; y<arrValue.length; y++) {
391                    attr.add(arrValue[y]);
392                }
393                attributes.put(attr);
394            }
395            return attributes;
396                    
397            }
398        
399        private String getAttrValueType(String attribute) {
400            int eqIndex=attribute.indexOf("=");
401            if(eqIndex != -1) return attribute.substring(0, eqIndex).trim();
402            return null;
403        }
404        
405        private String[] getAttributesValues(String attribute, String separator) throws PageException {
406            String strValue = attribute.substring(attribute.indexOf("=") + 1);
407            if(strValue.length() == 0) return null;
408            return ListUtil.toStringArray(ListUtil.listToArrayRemoveEmpty(strValue,separator.equals(", ") ? "," : separator));
409        }
410                    
411    }