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 021package org.apache.directory.server.core.hash; 022 023 024import java.util.ArrayList; 025import java.util.Collections; 026import java.util.List; 027 028 029import org.apache.directory.api.ldap.model.constants.LdapSecurityConstants; 030import org.apache.directory.api.ldap.model.entry.Attribute; 031import org.apache.directory.api.ldap.model.entry.Modification; 032import org.apache.directory.api.ldap.model.entry.Value; 033import org.apache.directory.api.ldap.model.exception.LdapException; 034import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException; 035import org.apache.directory.api.ldap.model.password.PasswordUtil; 036import org.apache.directory.api.ldap.model.schema.AttributeType; 037import org.apache.directory.server.config.beans.HashInterceptorBean; 038import org.apache.directory.server.core.api.DirectoryService; 039import org.apache.directory.server.core.api.interceptor.BaseInterceptor; 040import org.apache.directory.server.core.api.interceptor.context.AddOperationContext; 041import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext; 042 043 044/** 045 * An interceptor to hash a configurable set of attributeType(s) using 046 * a configurable hashing algorithm. 047 * 048 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 049 */ 050public class ConfigurableHashingInterceptor extends BaseInterceptor 051{ 052 053 /** the hashing algorithm to be used */ 054 private HashInterceptorBean config; 055 private LdapSecurityConstants algorithm; 056 private List<AttributeType> attributeTypes; 057 058 059 /** 060 * Creates a new instance of ConfigurableHashingInterceptor which hashes the 061 * incoming non-hashed attributeType(s) using the given algorithm. 062 * 063 * @param config The configuration bean 064 */ 065 public ConfigurableHashingInterceptor( HashInterceptorBean config ) 066 { 067 this.config = config; 068 } 069 070 071 /** 072 * {@inheritDoc} 073 */ 074 @Override 075 public void add( AddOperationContext addContext ) throws LdapException 076 { 077 if ( algorithm == null ) 078 { 079 next( addContext ); 080 return; 081 } 082 083 for ( Attribute attribute : addContext.getEntry().getAttributes() ) 084 { 085 if ( attributeTypes.contains( attribute.getAttributeType() ) ) 086 { 087 includeHashed( attribute ); 088 } 089 } 090 091 next( addContext ); 092 } 093 094 095 public LdapSecurityConstants getAlgorithm() 096 { 097 return algorithm; 098 } 099 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}