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