001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.tag;
020
021import java.io.IOException;
022
023import javax.naming.NamingException;
024import javax.naming.directory.DirContext;
025import javax.naming.directory.SearchControls;
026
027import lucee.commons.lang.ClassException;
028import lucee.runtime.exp.ApplicationException;
029import lucee.runtime.exp.PageException;
030import lucee.runtime.ext.tag.TagImpl;
031import lucee.runtime.net.ldap.LDAPClient;
032import lucee.runtime.op.Caster;
033import lucee.runtime.tag.util.DeprecatedUtil;
034import lucee.runtime.type.Query;
035import lucee.runtime.type.util.ArrayUtil;
036import lucee.runtime.type.util.ListUtil;
037
038// TODO tag ldap 
039// attr rebind
040
041/**
042* Provides an interface to LDAP Lightweight Directory Access Protocol 
043* directory servers like the Netscape Directory Server.
044*/
045public final class Ldap extends TagImpl {
046
047    private String delimiter=";";    
048    private String server;
049    private int port=389;
050    private short secureLevel=LDAPClient.SECURE_NONE;
051    private String[] returnAsBinary=new String[0];
052    private String attributes=null;
053    private String username;
054    private String password;
055    private String action="query";
056    private String[] sort=new String[0];
057    private String dn;
058    private int referral; 
059    private int scope=SearchControls.SUBTREE_SCOPE;
060    
061    private int sortType=LDAPClient.SORT_TYPE_CASE;
062    private int sortDirection=LDAPClient.SORT_DIRECTION_ASC;
063    
064    private int startrow=1;
065    private int timeout=60000;
066    private int maxrows;
067    private String name; 
068    private String start;
069    private String separator=",";
070    private String filter="objectclass = *";
071    private int modifyType=DirContext.REPLACE_ATTRIBUTE;
072    private boolean rebind;
073
074
075    @Override
076    public void release() {
077        action="query";
078        delimiter=";";
079        port=389;
080        secureLevel=LDAPClient.SECURE_NONE;
081        returnAsBinary=new String[0];
082        username=null;
083        password=null;
084        referral=0;
085        attributes=null;
086        sort=new String[0];
087        dn=null;
088        name=null;
089        scope=SearchControls.SUBTREE_SCOPE;
090        
091        startrow=1;
092        timeout=60000;
093        maxrows=-1; 
094        
095        sortType=LDAPClient.SORT_TYPE_CASE;
096        sortDirection=LDAPClient.SORT_DIRECTION_ASC;
097        
098        start=null;
099        separator=",";
100        filter="objectclass = *";
101        modifyType=DirContext.REPLACE_ATTRIBUTE;
102        rebind=false;
103        
104        super.release();
105        
106    }
107
108
109    /**
110     * @param filterfile The filterfile to set.
111     * @throws ApplicationException 
112     */
113    public void setFilterfile(String filterfile) {
114                DeprecatedUtil.tagAttribute(pageContext,"LDAP", "filterfile");
115    }
116    
117    /** Specifies the character that cfldap uses to separate multiple 
118     * attribute name/value pairs when more than one attribute is specified 
119     * in the attribute attribute or the attribute that you want to use has 
120     * the default delimiter character, which is the semicolon (;), 
121     * such as mgrpmsgrejecttext;lang-en. The delimiter character is used by the query, 
122     * add, and modify action attributes, and is used by cfldap to output multi-value attributes
123     * @param delimiter delimiter to set
124     */ 
125    public void setDelimiter(String delimiter) {
126        this.delimiter = delimiter;
127    }
128    
129    /**
130     * Used in conjunction with action = "Query". Specifies the first row of the LDAP query to insert 
131     * into the query. The default is 1.
132     * @param startrow The startrow to set.
133     */
134    public void setStartrow(double startrow) {
135        this.startrow = (int)startrow;
136    }
137
138    /**
139     * Specifies the maximum number of entries for LDAP queries.
140     * @param maxrows The maxrows to set.
141     */
142    public void setMaxrows(double maxrows) {
143        this.maxrows = (int)maxrows;
144    }
145
146    /**
147     * Specifies the maximum amount of time, in seconds, to wait for LDAP processing. Defaults to 
148     * 60 seconds.
149     * @param timeout The timeout to set.
150     */
151    public void setTimeout(double timeout) {
152        this.timeout = (int)timeout;
153    }
154    
155    /**
156     * @param password The password to set.
157     */
158    public void setPassword(String password) {
159        this.password = password;
160    }
161
162
163    /**
164     * Port defaults to the standard LDAP port, 389.
165     * @param port The port to set.
166     */
167    public void setPort(double port) {
168        this.port = (int) port;
169    }
170
171
172    /**
173     * Identifies the type of security to employ, CFSSL_BASIC or CFSSL_CLIENT_AUTH, 
174     * and additional information that is required by the specified security type.
175     * @param referral The referral to set.
176     */
177    public void setReferral(double referral) {
178        this.referral = (int) referral;
179    }
180
181
182    /**
183     * Host name "biff.upperlip.com" or IP address "192.1.2.225" of the LDAP server.
184     * @param server The server to set.
185     */
186    public void setServer(String server) {
187        this.server = server;
188    }
189
190
191    /**
192     * If no user name is specified, the LDAP connection is anonymous.
193     * @param username The username to set.
194     */
195    public void setUsername(String username) {
196        this.username = username;
197    }
198
199
200    /**
201     * @param secure The secureLevel to set.
202     * @throws ApplicationException 
203     */
204    public void setSecure(String secure) throws ApplicationException {
205        secure=secure.trim().toUpperCase();
206        if(secure.equals("CFSSL_BASIC")) secureLevel=LDAPClient.SECURE_CFSSL_BASIC;
207        else if(secure.equals("CFSSL_CLIENT_AUTH")) secureLevel=LDAPClient.SECURE_CFSSL_CLIENT_AUTH;
208        else throw new ApplicationException("invalid value for attribute secure ["+secure+"], valid values are [CFSSL_BASIC, CFSSL_CLIENT_AUTH]");
209    }
210    /**
211     * Specifies the scope of the search from the entry specified in the Start attribute 
212     * for action = "Query".
213     * @param strScope The scope to set.
214     * @throws ApplicationException 
215     */
216    public void setScope(String strScope) throws ApplicationException {
217        strScope=strScope.trim().toLowerCase();
218        if(strScope.equals("onelevel")) scope=SearchControls.ONELEVEL_SCOPE;
219        else if(strScope.equals("base")) scope=SearchControls.OBJECT_SCOPE;
220        else if(strScope.equals("subtree")) scope=SearchControls.SUBTREE_SCOPE;
221        else throw new ApplicationException("invalid value for attribute scope ["+strScope+"], valid values are [oneLevel,base,subtree]");
222    }
223    
224    /**
225     * Indicates whether to add, delete, or replace an attribute 
226     * in a multi-value list of attributes.
227     * @param modifyType The modifyType to set.
228     * @throws ApplicationException 
229     */ 
230    public void setModifytype(String modifyType) throws ApplicationException {
231        modifyType=modifyType.trim().toLowerCase();
232        if(modifyType.equals("add")) this.modifyType=DirContext.ADD_ATTRIBUTE;
233        else if(modifyType.equals("delete")) this.modifyType=DirContext.REMOVE_ATTRIBUTE;
234        else if(modifyType.equals("replace")) this.modifyType=DirContext.REPLACE_ATTRIBUTE;
235        else throw new ApplicationException("invalid value for attribute modifyType ["+modifyType+"], valid values are [add,replace,delete]");
236    }
237    
238    /**
239     * @param returnAsBinary The returnAsBinary to set.
240     * @throws PageException 
241     */
242    public void setReturnasbinary(String returnAsBinary) throws PageException {
243        this.returnAsBinary = ArrayUtil.trim(ListUtil.toStringArray(ListUtil.listToArrayRemoveEmpty(returnAsBinary,',')));
244    }
245    
246    /**
247     * Indicates the attribute or attributes by which to sort query results. 
248     * Use a comma [,] to separate attributes.
249     * @param sort The sort to set.
250     * @throws PageException 
251     */
252    public void setSort(String sort) throws PageException {
253        this.sort = ArrayUtil.trim(ListUtil.toStringArray(ListUtil.listToArrayRemoveEmpty(sort,','))); 
254    }
255    
256    /**
257     * Specifies how to sort query results.
258     * @param sortControl sortControl to set
259     * @throws PageException 
260     */
261    public void setSortcontrol(String sortControl) throws PageException {
262        String[] sortControlArr = ArrayUtil.trim(ListUtil.toStringArray(ListUtil.listToArrayRemoveEmpty(sortControl,','))); 
263        for(int i=0;i<sortControlArr.length;i++) {
264            String scs=sortControlArr[i].trim().toLowerCase();
265            
266            if(scs.equals("asc"))sortDirection=LDAPClient.SORT_DIRECTION_ASC;
267            else if(scs.equals("desc")) sortDirection=LDAPClient.SORT_DIRECTION_DESC;
268            else if(scs.equals("case"))sortType=LDAPClient.SORT_TYPE_CASE;
269            else if(scs.equals("nocase"))sortType=LDAPClient.SORT_TYPE_NOCASE;
270            else throw new ApplicationException("invalid value for attribute sortControl ["+sortControl+"], " +
271                    "valid values are [asc,desc,case,nocase]");
272        }
273    }
274    
275    /**
276     * @param strAttributes
277     */
278    public void setAttributes(String strAttributes) {
279        attributes = strAttributes;
280    }
281    
282    /**
283     * Specifies the LDAP action.
284     * @param action The action to set.
285     */
286    public void setAction(String action) {
287        this.action=action.trim().toLowerCase();
288    }
289
290
291    /**
292     * Specifies the distinguished name for update actions.
293     * @param dn The dn to set.
294     */
295    public void setDn(String dn) {
296        this.dn = dn;
297    }
298
299
300    /**
301     * The name you assign to the LDAP query.
302     * @param name The name to set.
303     */
304    public void setName(String name) {
305        this.name = name;
306    }
307
308
309    /**
310     * Specifies the character that cfldap uses to separate attribute values in multi-value attributes. 
311     * This character is used by the query, add, and modify action attributes, and 
312     * by cfldap to output multi-value attributes. The default character is the comma (,).
313     * @param separator The separator to set.
314     */
315    public void setSeparator(String separator) {
316        this.separator = separator;
317    }
318
319
320    /**
321     * Specifies the distinguished name of the entry to be used to start the search.
322     * @param start The start to set.
323     */
324    public void setStart(String start) {
325        this.start = start;
326    }
327
328
329    /**
330     * @param filter The filter to set.
331     */
332    public void setFilter(String filter) {
333        this.filter = filter;
334    }
335
336    /**
337     * If you set rebind to Yes, cfldap attempts to rebind the referral callback and reissue the query 
338     * by the referred address using the original credentials. The default is No, which means referred 
339     * connections are anonymous.
340     * @param rebind The rebind to set.
341     */
342    public void setRebind(boolean rebind) {
343        this.rebind = rebind;
344    }
345    
346
347    
348    
349    @Override
350    public int doStartTag() throws PageException {
351        try {
352            return _doStartTag();
353        } 
354        catch (Exception e) {
355            throw Caster.toPageException(e);
356        }
357    }
358    private int _doStartTag() throws NamingException, PageException, IOException, ClassException {
359        
360        //LDAPClient client=new LDAPClient(server,port,secureLevel,returnAsBinary,username,password,referral);
361        LDAPClient client=new LDAPClient(server,port,returnAsBinary);
362        if(secureLevel!=LDAPClient.SECURE_NONE)client.setSecureLevel(secureLevel);
363        if(username!=null)client.setCredential(username,password);
364        if(referral>0) client.setReferral(referral);
365        
366        if(action.equals("add")) {
367            required("LDAP",action,"attributes",attributes);
368            required("LDAP",action,"dn",dn);
369            client.add(dn,attributes,delimiter,separator);
370        }
371        else if(action.equals("delete")) {
372            required("LDAP",action,"dn",dn);
373            client.delete(dn);
374        }
375        else if(action.equals("modifydn")) {
376            required("LDAP",action,"attributes",attributes);
377            required("LDAP",action,"dn",dn);
378            client.modifydn(dn,attributes);
379        }
380        else if(action.equals("modify")) {
381            required("LDAP",action,"attributes",attributes);
382            required("LDAP",action,"dn",dn);
383            client.modify(dn,modifyType,attributes,delimiter,separator);
384        }
385        else if(action.equals("query")) {
386            required("LDAP",action,"start",start);
387            required("LDAP",action,"attributes",attributes);
388            required("LDAP",action,"name",name);
389            Query qry = client.query(attributes,scope,startrow,maxrows,
390                    timeout,sort,sortType,sortDirection,start,separator,filter);
391            pageContext.setVariable(name,qry);
392            
393        } 
394        else throw new ApplicationException("invalid value for attribute action ["+action+"], valid values are [add,delete,modifydn,modify,query]");
395        
396        return SKIP_BODY;
397    }
398
399
400
401
402}