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.ArrayList;
25  import java.util.Collections;
26  import java.util.List;
27  
28  
29  import org.apache.directory.api.ldap.model.constants.LdapSecurityConstants;
30  import org.apache.directory.api.ldap.model.entry.Attribute;
31  import org.apache.directory.api.ldap.model.entry.Modification;
32  import org.apache.directory.api.ldap.model.entry.Value;
33  import org.apache.directory.api.ldap.model.exception.LdapException;
34  import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
35  import org.apache.directory.api.ldap.model.password.PasswordUtil;
36  import org.apache.directory.api.ldap.model.schema.AttributeType;
37  import org.apache.directory.server.config.beans.HashInterceptorBean;
38  import org.apache.directory.server.core.api.DirectoryService;
39  import org.apache.directory.server.core.api.interceptor.BaseInterceptor;
40  import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
41  import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
42  
43  
44  /**
45   * An interceptor to hash a configurable set of attributeType(s) using
46   * a configurable hashing algorithm.
47   *
48   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
49   */
50  public class ConfigurableHashingInterceptor extends BaseInterceptor
51  {
52  
53      /** the hashing algorithm to be used */
54      private HashInterceptorBean config;
55      private LdapSecurityConstants algorithm;
56      private List<AttributeType> attributeTypes;
57  
58  
59      /**
60       * Creates a new instance of ConfigurableHashingInterceptor which hashes the
61       * incoming non-hashed attributeType(s) using the given algorithm.
62       * 
63       * @param config The configuration bean
64       */
65      public ConfigurableHashingInterceptor( HashInterceptorBean config )
66      {
67          this.config = config;
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          for ( Attribute attribute : addContext.getEntry().getAttributes() ) 
84          {
85              if ( attributeTypes.contains( attribute.getAttributeType() ) ) 
86              {
87                  includeHashed( attribute );    
88              }
89          }
90          
91          next( addContext );
92      }
93      
94      
95      public LdapSecurityConstants getAlgorithm() 
96      {
97          return algorithm;
98      }
99      
100     
101     public List<AttributeType> getAttributeTypes()
102     {
103         return Collections.unmodifiableList( attributeTypes );
104     }
105     
106     
107     private void includeHashed( Attribute attribute ) throws LdapInvalidAttributeValueException 
108     {
109         if ( attribute == null ) 
110         {
111             return;
112         }
113 
114         // hash any values if necessary
115         List<byte[]> values = new ArrayList<>();
116         
117         for ( Value value : attribute ) 
118         {
119             byte[] bytes = value.getBytes();
120             
121             if ( bytes == null )
122             {
123                 // value may be empty, dont wanna attempt to hash empty
124                 continue;
125             }
126 
127             // check if the given field is already hashed
128             LdapSecurityConstants existingAlgo = PasswordUtil.findAlgorithm( bytes );
129             
130             if ( existingAlgo == null ) 
131             {
132                 // not already hashed, so hash it
133                 values.add( PasswordUtil.createStoragePassword( bytes, algorithm ) );
134             }
135             else 
136             {
137                 // already hashed, just pass through
138                 values.add( bytes );
139             }
140         }
141         
142         // replace the value(s)
143         attribute.clear();
144         attribute.add( values.toArray( new byte[values.size()][] ) );
145     }
146 
147     
148     /**
149      * {@inheritDoc}
150      */
151     @Override
152     public void init( DirectoryService directoryService ) throws LdapException
153     {
154         // allow base initialization
155         super.init( directoryService );       
156 
157         // initialize from config
158         algorithm = LdapSecurityConstants.getAlgorithm( config.getHashAlgorithm() );
159         attributeTypes = new ArrayList<>();
160         for ( String attributeType : config.getHashAttributes() ) 
161         {
162             attributeTypes.add( schemaManager.lookupAttributeTypeRegistry( attributeType ) );
163         }
164     }
165 
166 
167     /**
168      * {@inheritDoc}
169      */
170     @Override
171     public void modify( ModifyOperationContext modifyContext ) throws LdapException
172     {
173         if ( algorithm == null )
174         {
175             next( modifyContext );
176             return;
177         }
178 
179         List<Modification> mods = modifyContext.getModItems();
180 
181         for ( Modification mod : mods ) 
182         {
183             Attribute attribute = mod.getAttribute();
184             if ( attributeTypes.contains( attribute.getAttributeType() ) )
185             {
186                 includeHashed( attribute );
187             }
188         }
189 
190         next( modifyContext );
191     }
192 }