View Javadoc
1   /*
2    *   Licensed to the Apache Software Foundation (ASF) under one
3    *   or more contributor license agreements.  See the NOTICE file
4    *   distributed with this work for additional information
5    *   regarding copyright ownership.  The ASF licenses this file
6    *   to you under the Apache License, Version 2.0 (the
7    *   "License"); you may not use this file except in compliance
8    *   with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing,
13   *   software distributed under the License is distributed on an
14   *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *   KIND, either express or implied.  See the License for the
16   *   specific language governing permissions and limitations
17   *   under the License.
18   *
19   */
20  
21  package org.apache.directory.server.core.hash;
22  
23  
24  import java.util.List;
25  
26  import org.apache.directory.api.ldap.model.constants.LdapSecurityConstants;
27  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
28  import org.apache.directory.api.ldap.model.entry.Attribute;
29  import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
30  import org.apache.directory.api.ldap.model.entry.Entry;
31  import org.apache.directory.api.ldap.model.entry.Modification;
32  import org.apache.directory.api.ldap.model.entry.ModificationOperation;
33  import org.apache.directory.api.ldap.model.entry.Value;
34  import org.apache.directory.api.ldap.model.exception.LdapException;
35  import org.apache.directory.api.ldap.model.password.PasswordUtil;
36  import org.apache.directory.api.util.Strings;
37  import org.apache.directory.server.core.api.interceptor.BaseInterceptor;
38  import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
39  import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
40  
41  
42  /**
43   * An interceptor to hash plain text password according to the configured
44   * hashing algorithm.
45   *
46   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
47   */
48  public abstract class PasswordHashingInterceptor extends BaseInterceptor
49  {
50  
51      /** the hashing algorithm to be used, if null then the password won't be changed */
52      private LdapSecurityConstants algorithm;
53  
54  
55      /**
56       * 
57       * Creates a new instance of PasswordHashingInterceptor which hashes the
58       * incoming non-hashed password using the given algorithm.
59       * If the password is found already hashed then it will skip hashing it.
60       * 
61       * @param name The instance's name
62       * @param algorithm the name of the algorithm to be used
63       */
64      protected PasswordHashingInterceptor( String name, LdapSecurityConstants algorithm )
65      {
66          super( name );
67          this.algorithm = algorithm;
68      }
69  
70  
71      /**
72       * {@inheritDoc}
73       */
74      @Override
75      public void add( AddOperationContext addContext ) throws LdapException
76      {
77          if ( algorithm == null )
78          {
79              next( addContext );
80              return;
81          }
82  
83          Entry entry = addContext.getEntry();
84  
85          Attribute pwdAt = entry.get( SchemaConstants.USER_PASSWORD_AT );
86  
87          Attribute hashedPwdAt = includeHashedPassword( pwdAt );
88          
89          if ( hashedPwdAt != null )
90          {
91              entry.remove( pwdAt );
92              entry.add( hashedPwdAt );
93          }
94  
95          next( addContext );
96      }
97  
98  
99      /**
100      * {@inheritDoc}
101      */
102     @Override
103     public void modify( ModifyOperationContext modifyContext ) throws LdapException
104     {
105         if ( algorithm == null )
106         {
107             next( modifyContext );
108             return;
109         }
110 
111         List<Modification> mods = modifyContext.getModItems();
112 
113         for ( Modification mod : mods )
114         {
115             String oid = mod.getAttribute().getAttributeType().getOid();
116 
117             // check for modification on 'userPassword' AT
118             if ( SchemaConstants.USER_PASSWORD_AT_OID.equals( oid ) )
119             {
120                 if ( mod.getOperation() == ModificationOperation.REMOVE_ATTRIBUTE )
121                 {
122                    continue; 
123                 }
124                 
125                 Attribute newPwd = includeHashedPassword( mod.getAttribute() );
126 
127                 if ( newPwd != null )
128                 {
129                     mod.setAttribute( newPwd );
130                 }
131             }
132         }
133 
134         next( modifyContext );
135     }
136 
137 
138     /**
139      * hash the password if it was <i>not</i> already hashed
140      *
141      * @param pwdAt the password attribute
142      */
143     private Attribute includeHashedPassword( Attribute pwdAt ) throws LdapException
144     {
145         if ( pwdAt == null )
146         {
147             return null;
148         }
149 
150         Attribute newPwd = new DefaultAttribute( pwdAt.getAttributeType() );
151 
152         // Special case : deal with a potential empty value. We may have more than one
153         for ( Value userPassword : pwdAt )
154         {
155             if ( Strings.isEmpty( userPassword.getString() ) )
156             {
157                 continue;
158             }
159 
160             // check if the given password is already hashed
161             LdapSecurityConstants existingAlgo = PasswordUtil.findAlgorithm( userPassword.getBytes() );
162 
163             // if there exists NO algorithm, then hash the password
164             if ( existingAlgo == null )
165             {
166                 byte[] hashedPassword = PasswordUtil.createStoragePassword( userPassword.getBytes(), algorithm );
167 
168                 newPwd.add( hashedPassword );
169             }
170             else
171             {
172                 newPwd.add( userPassword.getBytes() );
173             }
174         }
175 
176         return newPwd;
177     }
178 }