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 */ 017package org.apache.camel.processor; 018 019import java.io.PrintWriter; 020import java.io.StringWriter; 021import java.util.Arrays; 022import java.util.HashSet; 023import java.util.Map; 024import java.util.Set; 025import java.util.TreeMap; 026import java.util.concurrent.Future; 027import java.util.regex.Pattern; 028 029import org.apache.camel.Exchange; 030import org.apache.camel.Message; 031import org.apache.camel.spi.ExchangeFormatter; 032import org.apache.camel.spi.MaskingFormatter; 033import org.apache.camel.spi.UriParam; 034import org.apache.camel.spi.UriParams; 035import org.apache.camel.util.MessageHelper; 036import org.apache.camel.util.ObjectHelper; 037import org.apache.camel.util.StringHelper; 038 039/** 040 * The {@link MaskingFormatter} that searches the specified keywards in the source 041 * and replace its value with mask string. By default passphrase, password and secretKey 042 * are used as keywards to replace its value. 043 */ 044public class DefaultMaskingFormatter implements MaskingFormatter { 045 046 private static final Set<String> DEFAULT_KEYWORDS = new HashSet<String>(Arrays.asList("passphrase", "password", "secretKey")); 047 private Set<String> keywords; 048 private boolean maskKeyValue; 049 private boolean maskXmlElement; 050 private boolean maskJson; 051 private String maskString = "xxxxx"; 052 private Pattern keyValueMaskPattern; 053 private Pattern xmlElementMaskPattern; 054 private Pattern jsonMaskPattern; 055 056 public DefaultMaskingFormatter() { 057 this(DEFAULT_KEYWORDS, true, true, true); 058 } 059 060 public DefaultMaskingFormatter(boolean maskKeyValue, boolean maskXml, boolean maskJson) { 061 this(DEFAULT_KEYWORDS, maskKeyValue, maskXml, maskJson); 062 } 063 064 public DefaultMaskingFormatter(Set<String> keywords, boolean maskKeyValue, boolean maskXmlElement, boolean maskJson) { 065 this.keywords = keywords; 066 setMaskKeyValue(maskKeyValue); 067 setMaskXmlElement(maskXmlElement); 068 setMaskJson(maskJson); 069 } 070 071 public String format(String source) { 072 if (keywords == null || keywords.isEmpty()) { 073 return source; 074 } 075 076 String answer = source; 077 if (maskKeyValue) { 078 answer = keyValueMaskPattern.matcher(answer).replaceAll("$1\"" + maskString + "\""); 079 } 080 if (maskXmlElement) { 081 answer = xmlElementMaskPattern.matcher(answer).replaceAll("$1" + maskString + "$3"); 082 } 083 if (maskJson) { 084 answer = jsonMaskPattern.matcher(answer).replaceAll("$1\"" + maskString + "\""); 085 } 086 return answer; 087 } 088 089 public boolean isMaskKeyValue() { 090 return maskKeyValue; 091 } 092 093 public void setMaskKeyValue(boolean maskKeyValue) { 094 this.maskKeyValue = maskKeyValue; 095 if (maskKeyValue) { 096 keyValueMaskPattern = createKeyValueMaskPattern(keywords); 097 } else { 098 keyValueMaskPattern = null; 099 } 100 } 101 102 public boolean isMaskXmlElement() { 103 return maskXmlElement; 104 } 105 106 public void setMaskXmlElement(boolean maskXml) { 107 this.maskXmlElement = maskXml; 108 if (maskXml) { 109 xmlElementMaskPattern = createXmlElementMaskPattern(keywords); 110 } else { 111 xmlElementMaskPattern = null; 112 } 113 } 114 115 public boolean isMaskJson() { 116 return maskJson; 117 } 118 119 public void setMaskJson(boolean maskJson) { 120 this.maskJson = maskJson; 121 if (maskJson) { 122 jsonMaskPattern = createJsonMaskPattern(keywords); 123 } else { 124 jsonMaskPattern = null; 125 } 126 } 127 128 public String getMaskString() { 129 return maskString; 130 } 131 132 public void setMaskString(String maskString) { 133 this.maskString = maskString; 134 } 135 136 protected Pattern createKeyValueMaskPattern(Set<String> keywords) { 137 StringBuilder regex = createOneOfThemRegex(keywords); 138 if (regex == null) { 139 return null; 140 } 141 regex.insert(0, "([\\w]*(?:"); 142 regex.append(")[\\w]*[\\s]*?=[\\s]*?)([\\S&&[^'\",\\}\\]\\)]]+[\\S&&[^,\\}\\]\\)>]]*?|\"[^\"]*?\"|'[^']*?')"); 143 return Pattern.compile(regex.toString(), Pattern.CASE_INSENSITIVE); 144 } 145 146 protected Pattern createXmlElementMaskPattern(Set<String> keywords) { 147 StringBuilder regex = createOneOfThemRegex(keywords); 148 if (regex == null) { 149 return null; 150 } 151 regex.insert(0, "(<([\\w]*(?:"); 152 regex.append(")[\\w]*)(?:[\\s]+.+)*?>[\\s]*?)(?:[\\S&&[^<]]+(?:\\s+[\\S&&[^<]]+)*?)([\\s]*?</\\2>)"); 153 return Pattern.compile(regex.toString(), Pattern.CASE_INSENSITIVE); 154 } 155 156 protected Pattern createJsonMaskPattern(Set<String> keywords) { 157 StringBuilder regex = createOneOfThemRegex(keywords); 158 if (regex == null) { 159 return null; 160 } 161 regex.insert(0, "(\"(?:[^\"]|(?:\\\"))*?(?:"); 162 regex.append(")(?:[^\"]|(?:\\\"))*?\"\\s*?\\:\\s*?)(?:\"(?:[^\"]|(?:\\\"))*?\")"); 163 return Pattern.compile(regex.toString(), Pattern.CASE_INSENSITIVE); 164 } 165 166 protected StringBuilder createOneOfThemRegex(Set<String> keywords) { 167 StringBuilder regex = new StringBuilder(); 168 if (keywords == null || keywords.isEmpty()) { 169 return null; 170 } 171 String[] strKeywords = keywords.toArray(new String[0]); 172 regex.append(Pattern.quote(strKeywords[0])); 173 if (strKeywords.length > 1) { 174 for (int i = 1; i < strKeywords.length; i++) { 175 regex.append('|'); 176 regex.append(Pattern.quote(strKeywords[i])); 177 } 178 } 179 return regex; 180 } 181}