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.server.kerberos.shared.messages.value;
021    
022    
023    /**
024     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
025     * @version $Rev: 902575 $, $Date: 2010-01-24 15:38:06 +0100 (Sun, 24 Jan 2010) $
026     *
027    public class PrincipalName
028    {
029        private String nameComponent;
030        private int nameType;
031    
032    
033        /**
034         * Creates a new instance of PrincipalName.
035         *
036         * @param nameComponent
037         * @param nameType
038         *
039        public PrincipalName( String nameComponent, int nameType )
040        {
041            this.nameComponent = nameComponent;
042            this.nameType = nameType;
043        }
044    
045    
046        /**
047         * Returns the type of the {@link PrincipalName}.
048         *
049         * @return The type of the {@link PrincipalName}.
050         *
051        public int getNameType()
052        {
053            return nameType;
054        }
055    
056    
057        /**
058         * Returns the name component.
059         *
060         * @return The name component.
061         *
062        public String getNameComponent()
063        {
064            return nameComponent;
065        }
066    }*/
067    
068    /*
069     *  Licensed to the Apache Software Foundation (ASF) under one
070     *  or more contributor license agreements.  See the NOTICE file
071     *  distributed with this work for additional information
072     *  regarding copyright ownership.  The ASF licenses this file
073     *  to you under the Apache License, Version 2.0 (the
074     *  "License"); you may not use this file except in compliance
075     *  with the License.  You may obtain a copy of the License at
076     *  
077     *    http://www.apache.org/licenses/LICENSE-2.0
078     *  
079     *  Unless required by applicable law or agreed to in writing,
080     *  software distributed under the License is distributed on an
081     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
082     *  KIND, either express or implied.  See the License for the
083     *  specific language governing permissions and limitations
084     *  under the License. 
085     *  
086     */
087    package org.apache.directory.server.kerberos.shared.messages.value;
088    
089    
090    import java.nio.BufferOverflowException;
091    import java.nio.ByteBuffer;
092    import java.text.ParseException;
093    import java.util.ArrayList;
094    import java.util.List;
095    
096    import javax.security.auth.kerberos.KerberosPrincipal;
097    
098    import org.apache.directory.server.i18n.I18n;
099    import org.apache.directory.server.kerberos.shared.KerberosUtils;
100    import org.apache.directory.server.kerberos.shared.messages.value.types.PrincipalNameType;
101    import org.apache.directory.shared.asn1.AbstractAsn1Object;
102    import org.apache.directory.shared.asn1.ber.tlv.TLV;
103    import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
104    import org.apache.directory.shared.asn1.ber.tlv.Value;
105    import org.apache.directory.shared.asn1.codec.EncoderException;
106    import org.apache.directory.shared.ldap.util.StringTools;
107    import org.slf4j.Logger;
108    import org.slf4j.LoggerFactory;
109    
110    
111    /**
112     * A principal Name, composed of a type and N names.
113     * 
114     * PrincipalName   ::= SEQUENCE {
115     *        name-type       [0] Int32,
116     *        name-string     [1] SEQUENCE OF KerberosString
117     * }
118     *
119     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
120     * @version $Rev: 902575 $, $Date: 2010-01-24 15:38:06 +0100 (Sun, 24 Jan 2010) $
121     */
122    public class PrincipalName extends AbstractAsn1Object
123    {
124        /** The logger */
125        private static final Logger LOG = LoggerFactory.getLogger( PrincipalName.class );
126    
127        /** Speedup for logs */
128        private static final boolean IS_DEBUG = LOG.isDebugEnabled();
129    
130        /** The type for this principal */
131        private PrincipalNameType nameType;
132    
133        /** The principal name - we may have more than one - */
134        private List<String> nameString;
135        
136        /** The principal name as a byte[], for encoding purpose */
137        private transient List<byte[]> nameBytes;
138        
139        // Storage for computed lengths
140        private transient int principalNameSeqLength;
141        private transient int principalTypeTagLength;
142        private transient int principalTypeLength;
143        private transient int principalStringsTagLength;
144        private transient int principalStringsSeqLength;
145    
146        /**
147         * Creates a new empty instance of PrincipalName.
148         */
149        public PrincipalName()
150        {
151        }
152    
153        /**
154         * Creates a new instance of PrincipalName, given a KerberosPrincipal.
155         * 
156         * We assume that a principal has only one type, even if there are
157         * more than one name component.
158         *
159         * @param principal A Sun kerberosPrincipal instance
160         */
161        public PrincipalName( KerberosPrincipal principal )
162        {
163            try
164            {
165                nameString = KerberosUtils.getNames( principal );
166            }
167            catch ( ParseException pe )
168            {
169                nameString = KerberosUtils.EMPTY_PRINCIPAL_NAME;
170            }
171    
172            this.nameType = PrincipalNameType.getTypeByOrdinal( principal.getNameType() );
173        }
174        
175        /**
176         * Creates a new instance of PrincipalName given a String and an 
177         * prinipal type.
178         * 
179         * @param nameString The name string, which can contains more than one nameComponent
180         * @param nameType The principal name
181         */
182        public PrincipalName( String nameString, PrincipalNameType nameType )  throws ParseException
183        {
184            this.nameString = KerberosUtils.getNames( nameString );
185            
186            this.nameType = nameType;
187        }
188    
189    
190        /**
191         * Creates a new instance of PrincipalName.
192         *
193         * @param nameString
194         * @param nameType
195         */
196        public PrincipalName( String nameString, int nameType ) throws ParseException
197        {
198            this.nameString = KerberosUtils.getNames( nameString );
199            
200            this.nameType = PrincipalNameType.getTypeByOrdinal( nameType );
201        }
202    
203    
204        /**
205         * Returns the type of the {@link PrincipalName}.
206         *
207         * @return The type of the {@link PrincipalName}.
208         */
209        public PrincipalNameType getNameType()
210        {
211            return nameType;
212        }
213                        
214        /** 
215         * Set the Principal name Type
216         * @param nameType the Principal name Type
217         */
218        public void setNameType( PrincipalNameType nameType )
219        {
220            this.nameType = nameType;
221        }
222    
223        /** 
224         * Set the Principal name Type
225         * @param nameType the Principal name Type
226         */
227        public void setNameType( int nameType )
228        {
229            this.nameType = PrincipalNameType.getTypeByOrdinal( nameType );
230        }
231    
232        /**
233         * Returns the name components.
234         *
235         * @return The name components.
236         */
237        public List<String> getNames()
238        {
239            return nameString;
240        }
241    
242    
243        /**
244         * @return A String representing the principal names as a String 
245         */
246        public String getNameString()
247        {
248            if ( ( nameString == null ) || ( nameString.size() == 0 ) )
249            {
250                return "";
251            }
252            else
253            {
254                StringBuilder sb = new StringBuilder();
255                boolean isFirst = true;
256    
257                for ( String name : nameString )
258                {
259                    if ( isFirst )
260                    {
261                        isFirst = false;
262                    }
263                    else
264                    {
265                        sb.append( '/' );
266                    }
267    
268                    sb.append( name );
269                }
270    
271                return sb.toString();
272            }
273        }
274    
275    
276        /**
277         * Add a new name to the PrincipalName
278         * @param name The name to add
279         */
280        public void addName( String name )
281        {
282            if ( nameString == null )
283            {
284                nameString = new ArrayList<String>();
285            }
286    
287            nameString.add( name );
288        }
289    
290    
291        /**
292         * Compute the PrincipalName length
293         * 
294         * PrincipalName :
295         * 
296         * 0x30 L1 PrincipalName sequence
297         *  |
298         *  +--> 0xA1 L2 name-type tag
299         *  |     |
300         *  |     +--> 0x02 L2-1 addressType (int)
301         *  |
302         *  +--> 0xA2 L3 name-string tag
303         *        |
304         *        +--> 0x30 L3-1 name-string (SEQUENCE OF KerberosString)
305         *              |
306         *              +--> 0x1B L4[1] value (KerberosString)
307         *              |
308         *              +--> 0x1B L4[2] value (KerberosString)
309         *              |
310         *              ...
311         *              |
312         *              +--> 0x1B L4[n] value (KerberosString)
313         */
314        public int computeLength()
315        {
316            // The principalName can't be empty.
317            principalTypeLength = Value.getNbBytes( nameType.getOrdinal() );
318            principalTypeTagLength = 1 + TLV.getNbBytes( principalTypeLength ) + principalTypeLength;
319            
320            principalNameSeqLength = 1 + TLV.getNbBytes( principalTypeTagLength ) + principalTypeTagLength;
321    
322            // Compute the keyValue
323            if ( ( nameString == null ) || ( nameString.size() == 0 ) )
324            {
325                principalStringsSeqLength = 0;
326            }
327            else
328            {
329                principalStringsSeqLength = 0;
330                nameBytes = new ArrayList<byte[]>( nameString.size() );
331    
332                for ( String name : nameString )
333                {
334                    if ( name != null )
335                    {
336                        byte[] bytes = StringTools.getBytesUtf8( name );
337                        nameBytes.add( bytes );
338                        principalStringsSeqLength += 1 + TLV.getNbBytes( bytes.length ) + bytes.length;
339                    }
340                    else
341                    {
342                        nameBytes.add( StringTools.EMPTY_BYTES );
343                        principalStringsSeqLength += 1 + 1;
344                    }
345                }
346            }
347    
348            principalStringsTagLength = 1 + TLV.getNbBytes( principalStringsSeqLength ) + principalStringsSeqLength;
349            principalNameSeqLength += 1 + TLV.getNbBytes( principalStringsTagLength ) + principalStringsTagLength;
350    
351            // Compute the whole sequence length
352            return 1 + TLV.getNbBytes( principalNameSeqLength ) + principalNameSeqLength;
353        }
354    
355    
356        /**
357         * Encode the PrincipalName message to a PDU. 
358         * 
359         * PrincipalName :
360         * 
361         * 0x30 LL
362         *   0xA0 LL 
363         *     0x02 0x01 name-type (integer)
364         *   0xA1 LL 
365         *     0x30 LL name-string (SEQUENCE OF KerberosString)
366         *       0x1B LL name-string[1]
367         *       0x1B LL name-string[2]
368         *       ...
369         *       0x1B LL name-string[n]
370         * 
371         * @param buffer The buffer where to put the PDU. It should have been allocated
372         * before, with the right size.
373         * @return The constructed PDU.
374         */
375        public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
376        {
377            if ( buffer == null )
378            {
379                throw new EncoderException( I18n.err( I18n.ERR_148 ) );
380            }
381    
382            try
383            {
384                // The PrincipalName SEQ Tag
385                buffer.put( UniversalTag.SEQUENCE_TAG );
386                buffer.put( TLV.getBytes( principalNameSeqLength ) );
387    
388                // The name-type, first the tag, then the value
389                buffer.put( ( byte ) 0xA0 );
390                buffer.put( TLV.getBytes( principalTypeTagLength ) );
391                Value.encode( buffer, nameType.getOrdinal() );
392    
393                // The name-string tag
394                buffer.put( ( byte ) 0xA1 );
395                buffer.put( TLV.getBytes( principalStringsTagLength ) );
396    
397                // The name-string sequence
398                buffer.put( UniversalTag.SEQUENCE_TAG );
399    
400                if ( ( nameString == null ) || ( nameString.size() == 0 ) )
401                {
402                    buffer.put( ( byte ) 0x00 );
403                }
404                else
405                {
406                    buffer.put( TLV.getBytes( principalStringsSeqLength ) );
407    
408                    // The kerberosStrings
409                    for ( byte[] name : nameBytes )
410                    {
411                        buffer.put( UniversalTag.GENERALIZED_STRING_TAG );
412    
413                        if ( ( name == null ) || ( name.length == 0 ) )
414                        {
415                            buffer.put( ( byte ) 0x00 );
416                        }
417                        else
418                        {
419                            buffer.put( TLV.getBytes( name.length ) );
420                            buffer.put( name );
421                        }
422                    }
423                }
424            }
425            catch ( BufferOverflowException boe )
426            {
427                LOG.error( I18n.err( I18n.ERR_146, 1 + TLV.getNbBytes( principalNameSeqLength )
428                    + principalNameSeqLength, buffer.capacity() ) );
429                throw new EncoderException( I18n.err( I18n.ERR_138 ) );
430            }
431    
432            if ( IS_DEBUG )
433            {
434                LOG.debug( "PrinipalName encoding : {}", StringTools.dumpBytes( buffer.array() ) );
435                LOG.debug( "PrinipalName initial value : {}", toString() );
436            }
437    
438            return buffer;
439        }
440    
441    
442        /**
443         * @see Object#toString()
444         */
445        public String toString()
446        {
447            StringBuilder sb = new StringBuilder();
448    
449            sb.append( "PincipalName : {\n" );
450    
451            sb.append( "    name-type: " ).append( nameType ).append( '\n' );
452    
453            if ( ( nameString != null ) && ( nameString.size() != 0 ) )
454            {
455                sb.append( "    name-string : <" );
456                boolean isFirst = true;
457    
458                for ( String name : nameString )
459                {
460                    if ( isFirst )
461                    {
462                        isFirst = false;
463                    }
464                    else
465                    {
466                        sb.append( ", " );
467                    }
468    
469                    sb.append( '\'' ).append( name ).append( '\'' );
470                }
471    
472                sb.append( ">\n}" );
473            }
474            else
475            {
476                sb.append( "    no name-string\n}" );
477            }
478    
479            return sb.toString();
480        }
481    }