001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.shared.ldap.message;
021    
022    
023    import java.util.Arrays;
024    
025    import org.apache.directory.shared.i18n.I18n;
026    import org.apache.directory.shared.ldap.codec.MessageTypeEnum;
027    import org.apache.directory.shared.ldap.message.internal.InternalBindRequest;
028    import org.apache.directory.shared.ldap.message.internal.InternalBindResponse;
029    import org.apache.directory.shared.ldap.message.internal.InternalResultResponse;
030    import org.apache.directory.shared.ldap.name.DN;
031    import org.apache.directory.shared.ldap.util.StringTools;
032    
033    
034    /**
035     * Bind protocol operation request which authenticates and begins a client
036     * session. Does not yet contain interfaces for SASL authentication mechanisms.
037     * 
038     * @author <a href="mailto:dev@directory.apache.org"> Apache Directory Project</a>
039     * @version $Rev: 921600 $
040     */
041    public class BindRequestImpl extends AbstractAbandonableRequest implements InternalBindRequest
042    {
043        static final long serialVersionUID = 7945504184130380071L;
044    
045        /**
046         * Distinguished name identifying the name of the authenticating subject -
047         * defaults to the empty string
048         */
049        private DN name;
050    
051        /** The passwords, keys or tickets used to verify user identity */
052        private byte[] credentials;
053        
054        /** A storage for credentials hashCode */
055        private int hCredentials;
056    
057        /** The mechanism used to decode user identity */
058        private String mechanism;
059    
060        /** Simple vs. SASL authentication mode flag */
061        private boolean isSimple = true;
062    
063        /** Bind behavior exhibited by protocol version */
064        private boolean isVersion3 = true;
065    
066        /** The associated response */
067        public InternalBindResponse response;
068    
069    
070        // ------------------------------------------------------------------------
071        // Constructors
072        // ------------------------------------------------------------------------
073    
074        /**
075         * Creates an BindRequest implementation to bind to an LDAP server.
076         * 
077         * @param id the sequence identifier of the BindRequest message.
078         */
079        public BindRequestImpl( final int id )
080        {
081            super( id, TYPE );
082            hCredentials = 0;
083        }
084    
085    
086        // -----------------------------------------------------------------------
087        // BindRequest Interface Method Implementations
088        // -----------------------------------------------------------------------
089    
090        /**
091         * Checks to see if the authentication mechanism is simple and not SASL
092         * based.
093         * 
094         * @return true if the mechanism is simple false if it is SASL based.
095         */
096        public boolean isSimple()
097        {
098            return isSimple;
099        }
100    
101    
102        /**
103         * Checks to see if the authentication mechanism is simple and not SASL
104         * based.
105         * 
106         * @return true if the mechanism is simple false if it is SASL based.
107         */
108        public boolean getSimple()
109        {
110            return isSimple;
111        }
112    
113    
114        /**
115         * Sets the authentication mechanism to simple or to SASL based
116         * authentication.
117         * 
118         * @param isSimple
119         *            true if authentication is simple, false otherwise.
120         */
121        public void setSimple( boolean isSimple )
122        {
123            this.isSimple = isSimple;
124        }
125    
126    
127        /**
128         * Gets the simple credentials associated with a simple authentication
129         * attempt or null if this request uses SASL authentication mechanisms.
130         * 
131         * @return null if the mechanism is SASL or the credentials if it is simple.
132         */
133        public byte[] getCredentials()
134        {
135            return credentials;
136        }
137    
138    
139        /**
140         * Sets the simple credentials associated with a simple authentication
141         * attempt ignored if this request uses SASL authentication mechanisms.
142         * 
143         * @param credentials
144         *            the credentials if authentication is simple, null otherwise
145         */
146        public void setCredentials( byte[] credentials )
147        {
148            if ( credentials != null )
149            {
150                this.credentials = new byte[ credentials.length ];
151                System.arraycopy( credentials, 0, this.credentials, 0, credentials.length );
152            } 
153            else 
154            {
155                this.credentials = null;
156            }
157            
158            // Compute the hashcode
159            if ( credentials != null )
160            {
161                hCredentials = 0;
162                
163                for ( byte b:credentials )
164                {
165                    hCredentials = hCredentials*31 + b;
166                }
167            }
168            else
169            {
170                hCredentials = 0;
171            }
172        }
173    
174    
175        /**
176         * Gets the mechanism if this request uses SASL authentication mechanisms.
177         * 
178         * @return The mechanism if SASL.
179         */
180        public String getSaslMechanism()
181        {
182            return mechanism;
183        }
184    
185    
186        /**
187         * Sets the mechanism associated with a SASL authentication
188         * 
189         * @param mechanism
190         *            the mechanism otherwise
191         */
192        public void setSaslMechanism( String mechanism )
193        {
194            this.mechanism = mechanism;
195        }
196    
197    
198        /**
199         * Gets the distinguished name of the subject in this authentication
200         * request. This field may take on a null value (a zero length string) for
201         * the purposes of anonymous binds, when authentication has been performed
202         * at a lower layer, or when using SASL credentials with a mechanism that
203         * includes the DN in the credentials.
204         * 
205         * @return the DN of the authenticating user.
206         */
207        public DN getName()
208        {
209            return name;
210        }
211    
212    
213        /**
214         * Sets the distinguished name of the subject in this authentication
215         * request. This field may take on a null value (or a zero length string)
216         * for the purposes of anonymous binds, when authentication has been
217         * performed at a lower layer, or when using SASL credentials with a
218         * mechanism that includes the DN in the credentials.
219         * 
220         * @param name
221         *            the DN of the authenticating user - leave null for annonymous
222         *            user.
223         */
224        public void setName( DN name )
225        {
226            this.name = name;
227        }
228    
229    
230        /**
231         * Checks to see if the Ldap v3 protocol is used. Normally this would
232         * extract a version number from the bind request sent by the client
233         * indicating the version of the protocol to be used in this protocol
234         * session. The integer is either a 2 or a 3 at the moment. We thought it
235         * was better to just check if the protocol used is 3 or not rather than use
236         * an type-safe enumeration type for a binary value. If an LDAPv4 comes out
237         * then we shall convert the return type to a type safe enumeration.
238         * 
239         * @return true if client using version 3 false if it is version 2.
240         */
241        public boolean isVersion3()
242        {
243            return isVersion3;
244        }
245    
246    
247        /**
248         * Gets whether or not the Ldap v3 protocol is used. Normally this would
249         * extract a version number from the bind request sent by the client
250         * indicating the version of the protocol to be used in this protocol
251         * session. The integer is either a 2 or a 3 at the moment. We thought it
252         * was better to just check if the protocol used is 3 or not rather than use
253         * an type-safe enumeration type for a binary value. If an LDAPv4 comes out
254         * then we shall convert the return type to a type safe enumeration.
255         * 
256         * @return true if client using version 3 false if it is version 2.
257         */
258        public boolean getVersion3()
259        {
260            return isVersion3;
261        }
262    
263    
264        /**
265         * Sets whether or not the LDAP v3 or v2 protocol is used. Normally this
266         * would extract a version number from the bind request sent by the client
267         * indicating the version of the protocol to be used in this protocol
268         * session. The integer is either a 2 or a 3 at the moment. We thought it
269         * was better to just check if the protocol used is 3 or not rather than use
270         * an type-safe enumeration type for a binary value. If an LDAPv4 comes out
271         * then we shall convert the return type to a type safe enumeration.
272         * 
273         * @param isVersion3
274         *            if true the client will be exhibiting version 3 bind behavoir,
275         *            if false is used version 2 behavoir will be exhibited.
276         */
277        public void setVersion3( boolean isVersion3 )
278        {
279            this.isVersion3 = isVersion3;
280        }
281    
282    
283        // -----------------------------------------------------------------------
284        // BindRequest Interface Method Implementations
285        // -----------------------------------------------------------------------
286        /**
287         * Gets the protocol response message type for this request which produces
288         * at least one response.
289         * 
290         * @return the message type of the response.
291         */
292        public MessageTypeEnum getResponseType()
293        {
294            return RESP_TYPE;
295        }
296    
297    
298        /**
299         * The result containing response for this request.
300         * 
301         * @return the result containing response for this request
302         */
303        public InternalResultResponse getResultResponse()
304        {
305            if ( response == null )
306            {
307                response = new BindResponseImpl( getMessageId() );
308            }
309    
310            return response;
311        }
312        
313    
314    
315        /**
316         * RFC 2251/4511 [Section 4.11]: Abandon, Bind, Unbind, and StartTLS operations
317         * cannot be abandoned.
318         */
319        public void abandon()
320        {
321            throw new UnsupportedOperationException( I18n.err( I18n.ERR_04185 ) );
322        }
323    
324    
325        /**
326         * @see Object#equals(Object)
327         */
328        public boolean equals( Object obj )
329        {
330            if ( obj == this )
331            {
332                return true;
333            }
334    
335            if ( ( obj == null ) || !( obj instanceof InternalBindRequest ) )
336            {
337                return false;
338            }
339            
340            if ( !super.equals( obj ) )
341            {
342                return false;
343            }
344    
345            InternalBindRequest req = ( InternalBindRequest ) obj;
346    
347            if ( req.isSimple() != isSimple() )
348            {
349                return false;
350            }
351    
352            if ( req.isVersion3() != isVersion3() )
353            {
354                return false;
355            }
356    
357            DN dn1 = req.getName();
358            DN dn2 = getName();
359            
360            if ( dn1 == null )
361            {
362                if ( dn2 != null )
363                {
364                    return false;
365                }
366            }
367            else
368            {
369                if ( dn2 == null )
370                {
371                    return false;
372                }
373                else if ( !dn1.equals( dn2 ) )
374                {
375                    return false;
376                }
377                    
378            }
379            
380            if ( !Arrays.equals( req.getCredentials(), getCredentials() ) )
381            {
382                return false;
383            }
384    
385            return true;
386        }
387        
388        
389        /**
390         * @see Object#hashCode()
391         * @return the instance's hash code 
392         */
393        public int hashCode()
394        {
395            int hash = 37;
396            hash = hash*17 + ( credentials == null ? 0 : hCredentials );
397            hash = hash*17 + ( isSimple ? 0 : 1 );
398            hash = hash*17 + ( isVersion3 ? 0 : 1 );
399            hash = hash*17 + ( mechanism == null ? 0 : mechanism.hashCode() );
400            hash = hash*17 + ( name == null ? 0 : name.hashCode() );
401            hash = hash*17 + ( response == null ? 0 : response.hashCode() );
402            hash = hash*17 + super.hashCode();
403            
404            return hash;
405        }
406    
407        
408        /**
409         * Get a String representation of a BindRequest
410         * 
411         * @return A BindRequest String
412         */
413        public String toString()
414        {
415            StringBuffer sb = new StringBuffer();
416            sb.append( "    BindRequest\n" );
417            sb.append( "        Version : '" ).append( isVersion3 ? "3" : "2" ).append( "'\n" );
418    
419            if ( StringTools.isEmpty( name.getNormName() ) && isSimple )
420            {
421                sb.append( "        Name : anonymous\n" );
422            }
423            else
424            {
425                sb.append( "        Name : '" ).append( name.toString() ).append( "'\n" );
426    
427                if ( isSimple )
428                {
429                    sb.append( "        Simple authentication : '" ).append( StringTools.utf8ToString( credentials ) )
430                        .append( '/' ).append( StringTools.dumpBytes( credentials ) ).append( "'\n" );
431                }
432                else
433                {
434                    sb.append( "        Sasl credentials\n" );
435                    sb.append( "            Mechanism :'" ).append( mechanism ).append( "'\n" );
436                    
437                    if ( credentials == null )
438                    {
439                        sb.append( "            Credentials : null" );
440                    }
441                    else
442                    {
443                        sb.append( "            Credentials : '" ).
444                            append( StringTools.utf8ToString( credentials ) ).
445                            append( '/' ).
446                            append( StringTools.dumpBytes( credentials ) ).
447                            append( "'\n" );
448                    }
449                }
450            }
451    
452            return sb.toString();
453        }
454    }