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}