/** * Copyright (c) 2009--2014 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package com.redhat.rhn.frontend.xmlrpc; import com.redhat.rhn.common.hibernate.LookupException; import com.redhat.rhn.common.translation.Translator; import com.redhat.rhn.domain.session.InvalidSessionIdException; import com.redhat.rhn.domain.user.User; import com.redhat.rhn.domain.common.LoggingFactory; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.time.StopWatch; import org.apache.log4j.Logger; import java.util.List; import redstone.xmlrpc.XmlRpcInvocation; import redstone.xmlrpc.XmlRpcInvocationInterceptor; /** * LoggingInvocationProcessor extends the marquee-xmlrpc library to allow * us to log method calls. * @version $Rev$ */ public class LoggingInvocationProcessor implements XmlRpcInvocationInterceptor { private static Logger log = Logger.getLogger(LoggingInvocationProcessor.class); private static ThreadLocal caller = new ThreadLocal(); private static ThreadLocal timer = new ThreadLocal() { protected synchronized Object initialValue() { return new StopWatch(); } }; /** * {@inheritDoc} */ public boolean before(XmlRpcInvocation invocation) { // we start the timing and return true so processing // continues. // NOTE: as of commons-lang 2.1 we must reset before // starting. getStopWatch().reset(); getStopWatch().start(); List arguments = invocation.getArguments(); // HACK ALERT! We need the caller, would be better in // the postProcess, but that works for ALL methods except // logout. So we do it here. if ((arguments != null) && (arguments.size() > 0)) { if (arguments.get(0) instanceof User) { setCaller(((User)arguments.get(0)).getLogin()); } else { String arg = (String) Translator.convert( arguments.get(0), String.class); if (potentialSessionKey(arg)) { setCaller(getLoggedInUser(arg)); } } } return true; } /** * {@inheritDoc} */ public Object after(XmlRpcInvocation invocation, Object returnValue) { StringBuffer buf = new StringBuffer(); try { buf.append("REQUESTED FROM: "); buf.append(RhnXmlRpcServer.getCallerIp()); buf.append(" CALL: "); buf.append(invocation.getHandlerName()); buf.append("."); buf.append(invocation.getMethodName()); buf.append("("); processArguments(invocation.getHandlerName(), invocation.getMethodName(), invocation.getArguments(), buf); buf.append(") CALLER: ("); buf.append(getCaller()); buf.append(") TIME: "); getStopWatch().stop(); buf.append(getStopWatch().getTime() / 1000.00); buf.append(" seconds"); log.info(buf.toString()); } catch (RuntimeException e) { log.error("postProcess error CALL: " + invocation.getHandlerName() + " " + invocation.getMethodName(), e); } return returnValue; } /** * {@inheritDoc} */ public void onException(XmlRpcInvocation invocation, Throwable exception) { StringBuffer buf = new StringBuffer(); try { buf.append("REQUESTED FROM: "); buf.append(RhnXmlRpcServer.getCallerIp()); buf.append(" CALL: "); buf.append(invocation.getHandlerName()); buf.append("."); buf.append(invocation.getMethodName()); buf.append("("); processArguments(invocation.getHandlerName(), invocation.getMethodName(), invocation.getArguments(), buf); buf.append(") CALLER: ("); buf.append(getCaller()); buf.append(") TIME: "); getStopWatch().stop(); buf.append(getStopWatch().getTime() / 1000.00); buf.append(" seconds"); log.error(buf.toString(), exception); } catch (RuntimeException e) { log.error("postProcess error CALL: " + invocation.getHandlerName() + " " + invocation.getMethodName(), e); } } private void processArguments(String handler, String method, List arguments, StringBuffer buf) { // bug 199130: don't log password :) if ("auth.login".equals(handler + "." + method)) { if (arguments != null && arguments.size() > 0) { String arg = (String) Translator.convert( arguments.get(0), String.class); buf.append(arg); buf.append(", ********"); } } else { if (arguments != null) { int size = arguments.size(); for (int i = 0; i < size; i++) { String arg = null; if (arguments.get(i) instanceof User) { arg = ((User)arguments.get(i)).getLogin(); } else { arg = (String) Translator.convert( arguments.get(i), String.class); } buf.append(arg); if ((i + 1) < size) { buf.append(", "); } } } } } /** * If the key is a sessionKey, we'll return the username, otherwise we'll * return (unknown). * @param key potential sessionKey. * @return username, (Invalid Session ID), or (unknown); */ private String getLoggedInUser(String key) { try { User user = BaseHandler.getLoggedInUser(key); if (user != null) { LoggingFactory.setLogAuth(user.getId()); return user.getLogin(); } } catch (LookupException le) { // do nothing } catch (InvalidSessionIdException e) { return "(Invalid Session ID)"; } catch (Exception e) { log.error("problem with getting logged in user for logging", e); } return "(unknown)"; } /** * Returns true if the given key contains an 'x' which is the separator * character in the session key. * @param key Potential key candidate. * @return true if the given key contains an 'x' which is the separator * character in the session key. */ private boolean potentialSessionKey(String key) { if (key == null || key.equals("")) { return false; } // Get the id String[] keyParts = StringUtils.split(key, 'x'); // make sure the id is numeric and can be made into a Long if (!StringUtils.isNumeric(keyParts[0])) { return false; } return true; } private static StopWatch getStopWatch() { return (StopWatch) timer.get(); } private static String getCaller() { String c = (String) caller.get(); if (c == null) { return "none"; } return c; } private static void setCaller(String c) { caller.set(c); } }