/* * Copyright 2003 Federal Chancellery Austria * MOA-SPSS has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, and Graz University of Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. */ package at.gv.egovernment.moa.spss.server.logging; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import iaik.logging.TransactionId; /** * An implementation of the iaik.logging.Log interface that is * based on Jakarta Commons-Logging. * * @author Fatemeh Philippi * @version $Id$ */ public class IaikLog implements iaik.logging.Log { /** The hierarchy to log all IAIK output to. */ public static final String IAIK_LOG_HIERARCHY = "iaik.server"; /** The commons-loggin Log to use for logging the messages. */ private static Logger log = LoggerFactory.getLogger(IAIK_LOG_HIERARCHY); /** The node ID to use. */ private String nodeId; private static final Set LOGLEVEL_INFO_RECLASSIFICATION = Stream.of( "Max. cert info store size exceeded, consider using a larger certinfostore.") .collect(Collectors.toCollection(HashSet::new)); public static final String X509_INFO_CLEARING_PATTERN = "(?!serialNumber)(=)(.*?)(,|\"|$)"; private static Pattern multilinePattern; private static List maskPatterns = new ArrayList<>(); /** * Add log message that should be logged on INFO level instead of WARN. * *

IAIK-MOA and some other IAIK libs sometimes log on level WARN but it's only an info. * However, log level WARN can trigger wrong alerts in monitoring systems.

* * @param msg */ public static void addLogMsgForReclassification(String msg) { LOGLEVEL_INFO_RECLASSIFICATION.add(msg); } /** * Add masking pattern into logger. * * @param maskPattern */ public static void addMaskPattern(String maskPattern) { maskPatterns.add(maskPattern); multilinePattern = Pattern.compile( maskPatterns.stream() .collect(Collectors.joining("|")), Pattern.MULTILINE ); } /** * Create a new IaikLog. * * @param nodeId The node ID for this Log object. */ public IaikLog(String nodeId) { this.nodeId = nodeId; } /** * @see iaik.logging.Log#isDebugEnabled() */ @Override public boolean isDebugEnabled() { return log.isDebugEnabled(); } /** * @see iaik.logging.Log#debug(TransactionId, Object, Throwable) */ @Override public void debug(TransactionId transactionId, Object message, Throwable t) { final IaikLogMsg msg = new IaikLogMsg(transactionId, nodeId, message); log.debug(msg.toString(), t); } /** * @see iaik.logging.Log#isInfoEnabled() */ @Override public boolean isInfoEnabled() { return log.isInfoEnabled(); } /** * @see iaik.logging.Log#info(TransactionId, Object, Throwable) */ @Override public void info(TransactionId transactionId, Object message, Throwable t) { Object blankedMsg = log.isTraceEnabled() ? message : maskMessage(message); final IaikLogMsg msg = new IaikLogMsg(transactionId, nodeId, blankedMsg); log.info(msg.toString(), t); } /** * @see iaik.logging.Log#isWarnEnabled() */ @Override public boolean isWarnEnabled() { return log.isWarnEnabled(); } /** * @see iaik.logging.Log#warn(TransactionId, Object, Throwable) */ @Override public void warn(TransactionId transactionId, Object message, Throwable t) { Object blankedMsg = log.isTraceEnabled() ? message : maskMessage(message); final IaikLogMsg msg = new IaikLogMsg(transactionId, nodeId, blankedMsg); // log some messages on INFO. That's a work-around for suboptimal levels in third-party libs. if (LOGLEVEL_INFO_RECLASSIFICATION.contains(blankedMsg)) { log.info(msg.toString(), t); } else { log.warn(msg.toString(), t); } } /** * @see iaik.logging.Log#isErrorEnabled() */ @Override public boolean isErrorEnabled() { return log.isErrorEnabled(); } /** * @see iaik.logging.Log#error(TransactionId, Object, Throwable) */ @Override public void error(TransactionId transactionId, Object message, Throwable t) { Object blankedMsg = log.isTraceEnabled() ? message : maskMessage(message); final IaikLogMsg msg = new IaikLogMsg(transactionId, nodeId, blankedMsg); log.error(msg.toString(), t); } /** * @see iaik.logging.Log#isFatalEnabled() */ @Override public boolean isFatalEnabled() { return log.isErrorEnabled(); } /** * @see iaik.logging.Log#fatal(TransactionId, Object, Throwable) */ @Override public void fatal(TransactionId transactionId, Object message, Throwable t) { Object blankedMsg = log.isTraceEnabled() ? message : maskMessage(message); final IaikLogMsg msg = new IaikLogMsg(transactionId, nodeId, blankedMsg); log.error(msg.toString(), t); } /** * @see iaik.logging.Log#setNodeId(String) */ @Override public void setNodeId(String nodeId) { this.nodeId = nodeId; } /** * @see iaik.logging.Log#getNodeId() */ @Override public String getNodeId() { return nodeId; } private String maskMessage(Object message) { String msg = message != null ? message.toString() : ""; if (multilinePattern == null) { return msg; } StringBuilder sb = new StringBuilder(msg); Matcher matcher = multilinePattern.matcher(sb); while (matcher.find()) { IntStream.rangeClosed(1, matcher.groupCount()).forEach( group -> { if (matcher.group(group) != null) { IntStream.range(matcher.start(group), matcher.end(group)).forEach(i -> sb.setCharAt(i, '*')); // replace each character with asterisk } }); } return sb.toString(); } }