001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.modules.input;
018:
019: import org.apache.avalon.framework.configuration.Configuration;
020: import org.apache.avalon.framework.configuration.ConfigurationException;
021: import org.apache.avalon.framework.thread.ThreadSafe;
022:
023: import org.apache.cocoon.util.HashMap;
024:
025: import java.net.URLEncoder;
026: import java.security.MessageDigest;
027: import java.security.NoSuchAlgorithmException;
028: import java.security.NoSuchProviderException;
029: import java.util.Iterator;
030: import java.util.Map;
031: import java.io.UnsupportedEncodingException;
032:
033: /** Meta module that obtains values from other module and returns
034: * message digest of value. Very useful for storing and checking
035: * passwords. Input module configured through nested element
036: * "input-module", message digest algorithm, security provider, salt,
037: * and URL encoded output configurable through elements "algorithm",
038: * "provider", "salt", "encode". Defaults are "sha", null, "salt", and
039: * "false". Available value for encode are "none" (returns byte[]),
040: * "string" (return hash as string), "url" (returns url encoded
041: * string), "hex" (returns string of hex values).
042: *
043: * @author <a href="mailto:haul@apache.org">Christian Haul</a>
044: * @version CVS $Id: DigestMetaModule.java 433543 2006-08-22 06:22:54Z crossley $
045: */
046: public class DigestMetaModule extends AbstractMetaModule implements
047: ThreadSafe {
048:
049: private String defaultAlgorithm = "SHA";
050: private String defaultProvider = null;
051: private String defaultSalt = "salt";
052: private String defaultEncode = "false";
053:
054: /** output encoding none */
055: static final int ENCODING_NONE = 0;
056: /** output encoding url encoding */
057: static final int ENCODING_STR = 1;
058: /** output encoding hex */
059: static final int ENCODING_URL = 2;
060: /** output encoding hex */
061: static final int ENCODING_HEX = 3;
062:
063: private static final HashMap encodingNames;
064: /** setup mapping tables */
065: static {
066: HashMap names = new HashMap();
067: names.put("false", new Integer(ENCODING_NONE));
068: names.put("no", new Integer(ENCODING_NONE));
069: names.put("none", new Integer(ENCODING_NONE));
070: names.put("string", new Integer(ENCODING_STR));
071: names.put("yes", new Integer(ENCODING_URL));
072: names.put("true", new Integer(ENCODING_URL));
073: names.put("hex", new Integer(ENCODING_HEX));
074: encodingNames = names;
075: names = null;
076: }
077:
078: public void configure(Configuration config)
079: throws ConfigurationException {
080:
081: this .inputConf = config.getChild("input-module");
082: this .defaultInput = this .inputConf.getAttribute("name",
083: this .defaultInput);
084:
085: this .defaultAlgorithm = this .inputConf.getAttribute(
086: "algorithm", this .defaultAlgorithm);
087: this .defaultProvider = this .inputConf.getAttribute("provider",
088: this .defaultProvider);
089: this .defaultSalt = this .inputConf.getAttribute("salt",
090: this .defaultSalt);
091: this .defaultEncode = this .inputConf.getAttribute("encode",
092: "false");
093:
094: // preferred
095: this .defaultAlgorithm = config.getChild("algorithm").getValue(
096: this .defaultAlgorithm);
097: this .defaultProvider = config.getChild("provider").getValue(
098: this .defaultProvider);
099: this .defaultSalt = config.getChild("salt").getValue(
100: this .defaultSalt);
101: this .defaultEncode = config.getChild("encode").getValue(
102: this .defaultEncode);
103:
104: if (encodingNames.get(this .defaultEncode) == null) {
105: if (getLogger().isErrorEnabled())
106: getLogger().error(
107: "Requested encoding is unknown: "
108: + this .defaultEncode);
109: this .defaultEncode = "false";
110: }
111: }
112:
113: public Object getAttribute(String name, Configuration modeConf,
114: Map objectModel) throws ConfigurationException {
115:
116: if (!this .initialized) {
117: this .lazy_initialize();
118: }
119: if (this .defaultInput == null) {
120: if (getLogger().isWarnEnabled())
121: getLogger().warn("No input module given. FAILING");
122: return null;
123: }
124:
125: // obtain correct configuration objects
126: // default vs dynamic
127: Configuration inputConfig = null;
128: String inputName = null;
129: String algorithm = this .defaultAlgorithm;
130: String provider = this .defaultProvider;
131: String salt = this .defaultSalt;
132: int encode = ((Integer) encodingNames.get(this .defaultEncode))
133: .intValue();
134: if (modeConf != null) {
135: inputName = modeConf.getChild("input-module").getAttribute(
136: "name", null);
137: if (inputName != null) {
138: inputConfig = modeConf.getChild("input-module");
139: }
140: // read necessary parameters
141: algorithm = modeConf.getAttribute("algorithm", algorithm);
142: provider = modeConf.getAttribute("provider", provider);
143: salt = modeConf.getAttribute("salt", salt);
144: encode = ((Integer) encodingNames.get(modeConf
145: .getAttribute("encode", this .defaultEncode)))
146: .intValue();
147:
148: // preferred
149: algorithm = modeConf.getChild("algorithm").getValue(
150: algorithm);
151: provider = modeConf.getChild("provider").getValue(provider);
152: salt = modeConf.getChild("salt").getValue(salt);
153: encode = ((Integer) encodingNames.get(modeConf.getChild(
154: "encode").getValue(this .defaultEncode))).intValue();
155: }
156:
157: Object value = getValue(name, objectModel, this .input,
158: this .defaultInput, this .inputConf, null, inputName,
159: inputConfig);
160:
161: if (value != null)
162: try {
163: MessageDigest md = (provider == null ? MessageDigest
164: .getInstance(algorithm) : MessageDigest
165: .getInstance(algorithm, provider));
166:
167: md
168: .update((salt + (value instanceof String ? (String) value
169: : value.toString())).getBytes());
170: return encodeByteArray(md.digest(), encode);
171:
172: } catch (NoSuchAlgorithmException nsae) {
173: if (getLogger().isWarnEnabled())
174: getLogger().warn(
175: "A problem occurred acquiring digest algorithm '"
176: + algorithm
177: + (provider == null ? ""
178: : "' from '" + provider)
179: + "': " + nsae.getMessage());
180: } catch (NoSuchProviderException nspe) {
181: if (getLogger().isWarnEnabled())
182: getLogger().warn(
183: "A problem occurred acquiring digest algorithm '"
184: + algorithm
185: + (provider == null ? ""
186: : "' from '" + provider)
187: + "': " + nspe.getMessage());
188: }
189:
190: return null;
191: }
192:
193: public Iterator getAttributeNames(Configuration modeConf,
194: Map objectModel) throws ConfigurationException {
195:
196: if (!this .initialized) {
197: this .lazy_initialize();
198: }
199: if (this .defaultInput == null) {
200: if (getLogger().isWarnEnabled())
201: getLogger().warn("No input module given. FAILING");
202: return null;
203: }
204:
205: // obtain correct configuration objects
206: // default vs dynamic
207: Configuration inputConfig = null;
208: String inputName = null;
209: if (modeConf != null) {
210: inputName = modeConf.getChild("input-module").getAttribute(
211: "name", null);
212: if (inputName != null) {
213: inputConfig = modeConf.getChild("input-module");
214: }
215: }
216:
217: Iterator names = getNames(objectModel, this .input,
218: this .defaultInput, this .inputConf, null, inputName,
219: inputConfig);
220:
221: return names;
222:
223: }
224:
225: public Object[] getAttributeValues(String name,
226: Configuration modeConf, Map objectModel)
227: throws ConfigurationException {
228:
229: if (!this .initialized) {
230: this .lazy_initialize();
231: }
232: if (this .defaultInput == null) {
233: if (getLogger().isWarnEnabled())
234: getLogger().warn("No input module given. FAILING");
235: return null;
236: }
237:
238: // obtain correct configuration objects
239: // default vs dynamic
240: Configuration inputConfig = null;
241: String inputName = null;
242: String algorithm = this .defaultAlgorithm;
243: String provider = this .defaultProvider;
244: String salt = this .defaultSalt;
245: int encode = ((Integer) encodingNames.get(this .defaultEncode))
246: .intValue();
247: if (modeConf != null) {
248: inputName = modeConf.getChild("input-module").getAttribute(
249: "name", null);
250: if (inputName != null) {
251: inputConfig = modeConf.getChild("input-module");
252: }
253: // read necessary parameters
254: algorithm = modeConf.getAttribute("algorithm", algorithm);
255: provider = modeConf.getAttribute("provider", provider);
256: salt = modeConf.getAttribute("salt", salt);
257: encode = ((Integer) encodingNames.get(modeConf
258: .getAttribute("encode", this .defaultEncode)))
259: .intValue();
260:
261: // preferred
262: algorithm = modeConf.getChild("algorithm").getValue(
263: algorithm);
264: provider = modeConf.getChild("provider").getValue(provider);
265: salt = modeConf.getChild("salt").getValue(salt);
266: encode = ((Integer) encodingNames.get(modeConf.getChild(
267: "encode").getValue(this .defaultEncode))).intValue();
268: }
269:
270: Object[] values = getValues(name, objectModel, this .input,
271: this .defaultInput, this .inputConf, null, inputName,
272: inputConfig);
273: Object[] result = null;
274:
275: if (values != null) {
276: try {
277: MessageDigest md = (provider == null ? MessageDigest
278: .getInstance(algorithm) : MessageDigest
279: .getInstance(algorithm, provider));
280:
281: result = new Object[values.length];
282: for (int i = 0; i < values.length; i++) {
283: md
284: .update((salt + (values[i] instanceof String ? (String) values[i]
285: : values[i].toString())).getBytes());
286: result[i] = encodeByteArray(md.digest(), encode);
287: }
288: return result;
289: } catch (NoSuchAlgorithmException nsae) {
290: if (getLogger().isWarnEnabled())
291: getLogger().warn(
292: "A problem occurred acquiring digest algorithm '"
293: + algorithm
294: + (provider == null ? ""
295: : "' from '" + provider)
296: + "': " + nsae.getMessage());
297: } catch (NoSuchProviderException nspe) {
298: if (getLogger().isWarnEnabled())
299: getLogger().warn(
300: "A problem occurred acquiring digest algorithm '"
301: + algorithm
302: + (provider == null ? ""
303: : "' from '" + provider)
304: + "': " + nspe.getMessage());
305: }
306: }
307: return result;
308: }
309:
310: /**
311: * Create the output representation.
312: * @param b a <code>byte[]</code>
313: * @param encode an <code>int</code>, one of {@link #ENCODING_NONE},{@link #ENCODING_URL},{@link #ENCODING_HEX}
314: * @return an <code>Object</code>
315: */
316: Object encodeByteArray(byte[] b, int encode) {
317: Object result = null;
318: switch (encode) {
319: case ENCODING_HEX:
320: result = byte2Hex(b);
321: break;
322: case ENCODING_STR:
323: try {
324: result = new String(b, "UTF-8");
325: } catch (UnsupportedEncodingException uee) {
326: if (getLogger().isErrorEnabled())
327: getLogger()
328: .error(
329: "UTF-8 not supported -- cannot convert message digest to String.");
330: }
331: break;
332: case ENCODING_URL:
333: try {
334: String str = new String(b, "UTF-8");
335: result = URLEncoder.encode(str);
336: } catch (UnsupportedEncodingException uee) {
337: if (getLogger().isErrorEnabled())
338: getLogger()
339: .error(
340: "UTF-8 not supported -- cannot convert message digest to String.");
341: }
342: break;
343: case ENCODING_NONE:
344: // nothing to do
345: break;
346: default:
347: // should not happen
348: }
349: return result;
350: }
351:
352: /**
353: * Create a hex representation of a byte array.
354: *
355: * @param b a <code>byte[]</code> value
356: * @return a <code>String</code> value
357: */
358: static String byte2Hex(byte[] b) {
359: StringBuffer sb = new StringBuffer(b.length * 2);
360: for (int i = 0; i < b.length; i++) {
361: sb.append(hexChar[(b[i] & 0xf0) >>> 4]);
362: sb.append(hexChar[(b[i] & 0x0f)]);
363: }
364: return sb.toString();
365: }
366:
367: /**
368: * hex digits lookup table
369: */
370: static char[] hexChar = { '0', '1', '2', '3', '4', '5', '6', '7',
371: '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
372:
373: }
|