View Javadoc
1   /*
2    * Copyright (c) 2002-2021, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.accesslogger.service;
35  
36  import java.io.UnsupportedEncodingException;
37  import java.security.MessageDigest;
38  import java.security.NoSuchAlgorithmException;
39  
40  import org.apache.log4j.Logger;
41  
42  import com.fasterxml.jackson.core.JsonProcessingException;
43  import com.fasterxml.jackson.databind.ObjectMapper;
44  import fr.paris.lutece.portal.service.util.AppPropertiesService;
45  
46  import java.text.MessageFormat;
47  
48  /**
49   * This class provides writing services in the application logs files
50   */
51  public final class DefaultAccessLogger implements IAccessLogger
52  {
53  
54      private static final String CONSTANT_HASH_ENCODING = "UTF-8";
55      private static final String CONSTANT_HASH_DIGEST = "MD5";
56      private static final String PROPERTY_ADD_HASH_TO_LOGS = "portal.defaultAccessLogger.addHashToLogs";
57      private static final String PROPERTY_ACCESSLOG_MESSAGE_FORMAT = "portal.defaultAccessLogger.messageFormat";
58      private static final String PROPERTY_ACCESSLOG_MESSAGE_FORMAT_SEPARATOR = "portal.defaultAccessLogger.messageFormatSeparator";
59  
60      private static final String DEFAULT_ACCESSLOG_MESSAGE_FORMAT = "|{0}|{1}|{2}|{3}|{4}|{5}|{6}|";
61      private static final String DEFAULT_ACCESSLOG_MESSAGE_FORMAT_SEPARATOR = "|";
62  
63      private final boolean _bAddHashToLogs = AppPropertiesService.getPropertyBoolean( PROPERTY_ADD_HASH_TO_LOGS, false );
64      private final String _messageFormat = AppPropertiesService.getProperty( PROPERTY_ACCESSLOG_MESSAGE_FORMAT, DEFAULT_ACCESSLOG_MESSAGE_FORMAT );
65      private final String _messageFormatSeparator = AppPropertiesService.getProperty( PROPERTY_ACCESSLOG_MESSAGE_FORMAT_SEPARATOR,
66              DEFAULT_ACCESSLOG_MESSAGE_FORMAT_SEPARATOR );
67      private static Logger _logger = Logger.getLogger( LOGGER_ACCESS_LOG );
68      private static final String ERROR_MSG = "ERROR : unable to create json from data";
69  
70      /**
71       * Log a message object with the INFO level. It is logged in application.log
72       *
73       * @param strEventType
74       * @param strAppEventCode
75       * @param strConnectedUserLogin
76       * @param data
77       *            the message object to log
78       */
79      public void info( String strEventType, String strAppEventCode, String strConnectedUserLogin, Object data )
80      {
81          if ( _logger.isInfoEnabled( ) )
82          {
83              String strAppId = AppPropertiesService.getProperty( IAccessLogger.PROPERTY_SITE_CODE, "?" );
84              String logMessage = getLogMessage( strAppId, strEventType, strAppEventCode, strConnectedUserLogin, data );
85  
86              _logger.info( logMessage );
87          }
88      }
89  
90      /**
91       * Log a message object with the DEBUG level. It is logged in application.log
92       *
93       * @param strEventType
94       * @param strAppEventCode
95       * @param strConnectedUserLogin
96       * @param data
97       *            the message object to log
98       */
99      public void debug( String strEventType, String strAppEventCode, String strConnectedUserLogin, Object data )
100     {
101         if ( _logger.isDebugEnabled( ) )
102         {
103             String strAppId = AppPropertiesService.getProperty( PROPERTY_SITE_CODE, "?" );
104             String logMessage = getLogMessage( strAppId, strEventType, strAppEventCode, strConnectedUserLogin, data );
105 
106             _logger.debug( logMessage );
107         }
108     }
109 
110     /**
111      * Log a message object with the TRACE level.It is logged in application.log
112      *
113      * @param strEventType
114      * @param strAppEventCode
115      * @param strConnectedUserLogin
116      * @param data
117      */
118     public void trace( String strEventType, String strAppEventCode, String strConnectedUserLogin, Object data )
119     {
120         if ( _logger.isTraceEnabled( ) )
121         {
122             String strAppId = AppPropertiesService.getProperty( PROPERTY_SITE_CODE, "?" );
123             String logMessage = getLogMessage( strAppId, strEventType, strAppEventCode, strConnectedUserLogin, data );
124 
125             _logger.trace( logMessage );
126         }
127     }
128 
129     /**
130      * build log message
131      *
132      * @param eventType
133      * @param description
134      * @param connectedUserLogin
135      * @param data
136      * @return the log message
137      */
138     private String getLogMessage( String strAppId, String strEventType, String strAppEventCode, String strConnectedUserLogin, Object data )
139     {
140 
141         String jsonData = "";
142 
143         if ( data != null )
144         {
145             ObjectMapper obj = new ObjectMapper( );
146 
147             try
148             {
149                 jsonData = obj.writeValueAsString( data );
150             }
151             catch( JsonProcessingException e )
152             {
153                 jsonData = ERROR_MSG;
154             }
155         }
156 
157         return getLogMessage( strAppId, strEventType, strAppEventCode, strConnectedUserLogin, jsonData, isAddHashToLogs( ) );
158     }
159 
160     /**
161      * build log message
162      *
163      * @param eventType
164      * @param description
165      * @param connectedUserLogin
166      * @param data
167      * @return the log message
168      */
169     private String getLogMessage( String strAppId, String strEventType, String strAppEventCode, String strConnectedUserLogin, String strData,
170             boolean isAddHashToLogs )
171     {
172 
173         String strHash = "";
174 
175         if ( isAddHashToLogs )
176         {
177             strHash = getHash( MessageFormat.format( _messageFormat, "", strAppId, strEventType, strAppEventCode, strConnectedUserLogin, strData ) );
178         }
179 
180         return MessageFormat.format( _messageFormat, strHash, strAppId, strEventType, strAppEventCode, strConnectedUserLogin, strData, strHash );
181     }
182 
183     /**
184      * get hash
185      *
186      * @param message
187      * @param last
188      *            hash
189      *
190      * @return the hash in String
191      */
192     private static String getHash( String message )
193     {
194 
195         byte [ ] byteChaine;
196         try
197         {
198             byteChaine = message.getBytes( CONSTANT_HASH_ENCODING );
199             MessageDigest md = MessageDigest.getInstance( CONSTANT_HASH_DIGEST );
200             byte [ ] hash = md.digest( byteChaine );
201 
202             // convert byte array to Hexadecimal String
203             StringBuilder sb = new StringBuilder( 2 * hash.length );
204             for ( byte b : hash )
205             {
206                 sb.append( String.format( "%02x", b & 0xff ) );
207             }
208 
209             return sb.toString( );
210 
211         }
212         catch( UnsupportedEncodingException | NoSuchAlgorithmException e )
213         {
214             return "Hash ERROR : " + e.getLocalizedMessage( );
215         }
216 
217     }
218 
219     /**
220      * verify hash
221      *
222      * @param message
223      *
224      * @return true if the hash contained in the message is valid
225      */
226     public boolean verifyMessageHash( String message )
227     {
228         try
229         {
230 
231             int idx = message.indexOf( _messageFormatSeparator, message.indexOf( LOGGER_ACCESS_LOG ) );
232             String hash = message.substring( idx + 1, idx + 33 );
233             String data = message.substring( idx + 34 );
234 
235             return ( hash != null && ( hash.equals( "" ) || hash.equals( getHash( data ) ) ) );
236 
237         }
238         catch( StringIndexOutOfBoundsException e )
239         {
240 
241             return false;
242         }
243     }
244 
245     /**
246      * is addHashToLogs enabled
247      * 
248      * @return true if addHashToLogs enabled
249      */
250     public boolean isAddHashToLogs( )
251     {
252         return _bAddHashToLogs;
253     }
254 
255 }