blob: b0e03efb1576521b08657bf5cab42f7272735e0e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cocoon.components.modules.input;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.cocoon.util.HashMap;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Iterator;
import java.util.Map;
import java.io.UnsupportedEncodingException;
/** Meta module that obtains values from other module and returns
* message digest of value. Very useful for storing and checking
* passwords. Input module configured through nested element
* "input-module", message digest algorithm, security provider, salt,
* and URL encoded output configurable through elements "algorithm",
* "provider", "salt", "encode". Defaults are "sha", null, "salt", and
* "false". Available value for encode are "none" (returns byte[]),
* "string" (return hash as string), "url" (returns url encoded
* string), "hex" (returns string of hex values).
*
* @author <a href="mailto:haul@apache.org">Christian Haul</a>
* @version CVS $Id$
*/
public class DigestMetaModule extends AbstractMetaModule implements ThreadSafe {
private String defaultAlgorithm = "SHA";
private String defaultProvider = null;
private String defaultSalt = "salt";
private String defaultEncode = "false";
/** output encoding none */
static final int ENCODING_NONE = 0;
/** output encoding url encoding */
static final int ENCODING_STR = 1;
/** output encoding hex */
static final int ENCODING_URL = 2;
/** output encoding hex */
static final int ENCODING_HEX = 3;
private static final HashMap encodingNames;
/** setup mapping tables */
static {
HashMap names = new HashMap();
names.put("false", new Integer(ENCODING_NONE));
names.put("no", new Integer(ENCODING_NONE));
names.put("none", new Integer(ENCODING_NONE));
names.put("string", new Integer(ENCODING_STR));
names.put("yes", new Integer(ENCODING_URL));
names.put("true", new Integer(ENCODING_URL));
names.put("hex", new Integer(ENCODING_HEX));
encodingNames = names;
names = null;
}
public void configure(Configuration config) throws ConfigurationException {
this.inputConf = config.getChild("input-module");
this.defaultInput = this.inputConf.getAttribute("name", this.defaultInput);
this.defaultAlgorithm = this.inputConf.getAttribute("algorithm",this.defaultAlgorithm);
this.defaultProvider = this.inputConf.getAttribute("provider",this.defaultProvider);
this.defaultSalt = this.inputConf.getAttribute("salt",this.defaultSalt);
this.defaultEncode = this.inputConf.getAttribute("encode","false");
// preferred
this.defaultAlgorithm = config.getChild("algorithm").getValue(this.defaultAlgorithm);
this.defaultProvider = config.getChild("provider").getValue(this.defaultProvider);
this.defaultSalt = config.getChild("salt").getValue(this.defaultSalt);
this.defaultEncode = config.getChild("encode").getValue(this.defaultEncode);
if (encodingNames.get(this.defaultEncode) == null) {
if (getLogger().isErrorEnabled())
getLogger().error("Requested encoding is unknown: "+this.defaultEncode);
this.defaultEncode="false";
}
}
public Object getAttribute( String name, Configuration modeConf, Map objectModel )
throws ConfigurationException {
if (!this.initialized) {
this.lazy_initialize();
}
if (this.defaultInput == null) {
if (getLogger().isWarnEnabled())
getLogger().warn("No input module given. FAILING");
return null;
}
// obtain correct configuration objects
// default vs dynamic
Configuration inputConfig = null;
String inputName=null;
String algorithm = this.defaultAlgorithm;
String provider = this.defaultProvider;
String salt = this.defaultSalt;
int encode = ((Integer) encodingNames.get(this.defaultEncode)).intValue();
if (modeConf!=null) {
inputName = modeConf.getChild("input-module").getAttribute("name",null);
if (inputName != null) {
inputConfig = modeConf.getChild("input-module");
}
// read necessary parameters
algorithm = modeConf.getAttribute("algorithm", algorithm);
provider = modeConf.getAttribute("provider", provider);
salt = modeConf.getAttribute("salt", salt);
encode = ((Integer) encodingNames.get(modeConf.getAttribute("encode", this.defaultEncode))).intValue();
// preferred
algorithm = modeConf.getChild("algorithm").getValue(algorithm);
provider = modeConf.getChild("provider").getValue(provider);
salt = modeConf.getChild("salt").getValue(salt);
encode = ((Integer) encodingNames.get(modeConf.getChild("encode").getValue(this.defaultEncode))).intValue();
}
Object value = getValue(name, objectModel,
this.input, this.defaultInput, this.inputConf,
null, inputName, inputConfig);
if (value != null)
try {
MessageDigest md = (provider==null ? MessageDigest.getInstance(algorithm) :
MessageDigest.getInstance(algorithm,provider));
md.update((salt+(value instanceof String? (String)value : value.toString())).getBytes());
return encodeByteArray(md.digest(),encode);
} catch (NoSuchAlgorithmException nsae) {
if (getLogger().isWarnEnabled())
getLogger().warn("A problem occurred acquiring digest algorithm '" + algorithm
+ (provider==null?"":"' from '"+provider) +"': " + nsae.getMessage());
} catch (NoSuchProviderException nspe) {
if (getLogger().isWarnEnabled())
getLogger().warn("A problem occurred acquiring digest algorithm '" + algorithm
+ (provider==null?"":"' from '"+provider) +"': " + nspe.getMessage());
}
return null;
}
public Iterator getAttributeNames( Configuration modeConf, Map objectModel )
throws ConfigurationException {
if (!this.initialized) {
this.lazy_initialize();
}
if (this.defaultInput == null) {
if (getLogger().isWarnEnabled())
getLogger().warn("No input module given. FAILING");
return null;
}
// obtain correct configuration objects
// default vs dynamic
Configuration inputConfig = null;
String inputName=null;
if (modeConf!=null) {
inputName = modeConf.getChild("input-module").getAttribute("name",null);
if (inputName != null) {
inputConfig = modeConf.getChild("input-module");
}
}
Iterator names = getNames(objectModel,
this.input, this.defaultInput, this.inputConf,
null, inputName, inputConfig);
return names;
}
public Object[] getAttributeValues( String name, Configuration modeConf, Map objectModel )
throws ConfigurationException {
if (!this.initialized) {
this.lazy_initialize();
}
if (this.defaultInput == null) {
if (getLogger().isWarnEnabled())
getLogger().warn("No input module given. FAILING");
return null;
}
// obtain correct configuration objects
// default vs dynamic
Configuration inputConfig = null;
String inputName=null;
String algorithm = this.defaultAlgorithm;
String provider = this.defaultProvider;
String salt = this.defaultSalt;
int encode = ((Integer) encodingNames.get(this.defaultEncode)).intValue();
if (modeConf!=null) {
inputName = modeConf.getChild("input-module").getAttribute("name",null);
if (inputName != null) {
inputConfig = modeConf.getChild("input-module");
}
// read necessary parameters
algorithm = modeConf.getAttribute("algorithm", algorithm);
provider = modeConf.getAttribute("provider" , provider );
salt = modeConf.getAttribute("salt" , salt );
encode = ((Integer) encodingNames.get(modeConf.getAttribute("encode" , this.defaultEncode))).intValue();
// preferred
algorithm = modeConf.getChild("algorithm").getValue(algorithm);
provider = modeConf.getChild("provider").getValue(provider);
salt = modeConf.getChild("salt").getValue(salt);
encode = ((Integer) encodingNames.get(modeConf.getChild("encode").getValue(this.defaultEncode))).intValue();
}
Object[] values = getValues(name, objectModel,
this.input, this.defaultInput, this.inputConf,
null, inputName, inputConfig);
Object[] result = null;
if (values != null) {
try {
MessageDigest md = (provider==null ? MessageDigest.getInstance(algorithm) :
MessageDigest.getInstance(algorithm,provider));
result = new Object[values.length];
for (int i=0; i<values.length; i++) {
md.update((salt + (values[i] instanceof String? (String)values[i] :
values[i].toString())).getBytes());
result[i] = encodeByteArray(md.digest(), encode);
}
return result;
} catch (NoSuchAlgorithmException nsae) {
if (getLogger().isWarnEnabled())
getLogger().warn("A problem occurred acquiring digest algorithm '" + algorithm
+ (provider==null?"":"' from '"+provider) +"': " + nsae.getMessage());
} catch (NoSuchProviderException nspe) {
if (getLogger().isWarnEnabled())
getLogger().warn("A problem occurred acquiring digest algorithm '" + algorithm
+ (provider==null?"":"' from '"+provider) +"': " + nspe.getMessage());
}
}
return result;
}
/**
* Create the output representation.
* @param b a <code>byte[]</code>
* @param encode an <code>int</code>, one of {@link #ENCODING_NONE},{@link #ENCODING_URL},{@link #ENCODING_HEX}
* @return an <code>Object</code>
*/
Object encodeByteArray(byte[] b, int encode) {
Object result = null;
switch(encode) {
case ENCODING_HEX:
result = byte2Hex(b);
break;
case ENCODING_STR:
try {
result = new String(b, "UTF-8");
} catch (UnsupportedEncodingException uee) {
if (getLogger().isErrorEnabled())
getLogger().error("UTF-8 not supported -- cannot convert message digest to String.");
}
break;
case ENCODING_URL:
try {
String str = new String(b, "UTF-8");
result = URLEncoder.encode(str);
} catch (UnsupportedEncodingException uee) {
if (getLogger().isErrorEnabled())
getLogger().error("UTF-8 not supported -- cannot convert message digest to String.");
}
break;
case ENCODING_NONE:
// nothing to do
break;
default:
// should not happen
}
return result;
}
/**
* Create a hex representation of a byte array.
*
* @param b a <code>byte[]</code> value
* @return a <code>String</code> value
*/
static String byte2Hex ( byte[] b ) {
StringBuffer sb = new StringBuffer( b.length * 2 );
for ( int i=0 ; i < b.length ; i++ ) {
sb.append( hexChar [ ( b[i] & 0xf0 ) >>> 4 ] ) ;
sb.append( hexChar [ ( b[i] & 0x0f ) ] ) ;
}
return sb.toString() ;
}
/**
* hex digits lookup table
*/
static char[] hexChar = {
'0' , '1' , '2' , '3' ,
'4' , '5' , '6' , '7' ,
'8' , '9' , 'a' , 'b' ,
'c' , 'd' , 'e' , 'f'
};
}