All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jwall.web.audit.io.ConcurrentAuditWriter Maven / Gradle / Ivy

/*
 *  Copyright (C) 2007-2014 Christian Bockermann 
 *
 *  This file is part of the  web-audit  library.
 *
 *  web-audit library is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  The  web-audit  library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see .
 *
 */
package org.jwall.web.audit.io;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.jwall.web.audit.AuditEvent;
import org.jwall.web.audit.ModSecurity;
import org.jwall.web.audit.util.MD5;


/**
 * 
 * 
 * This class implements a simple writer that writes audit-events in
 * the concurrent log format of modsecurity. It can be used with the
 * AuditLogger tool to create concurrent audit-logs.
 * 
 * It can also be used to convert serial auditlogs into the concurrent
 * log format.
 * 
 * @author Christian Bockermann <[email protected]>
 *
 */
public class ConcurrentAuditWriter
implements AuditEventWriter
{
	/* this format maps the date to a corresponding directory (relative to the base-directory) */
    public final static SimpleDateFormat DIR_FORMAT = new SimpleDateFormat("yyyyMMdd/yyyyMMdd-HHmm");
    
    /* this format specifies the date-format used in the summary-line of the index-file */
    public final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("[dd/MMM/yyyy:HH:mm:ss Z]");
    
    /** the base-directory where the event-files are stored */
    private File dir = null;
    
    /** the path to the summary-index file */
    private File idx = null;

    /** counts the number of events, written by this writer */
    int count = 0;
    
    /** the writer that appends the summary lines to index-file */
    PrintStream indexWriter;
    

    /**
     * 
     * This constructor creates a new instance of the class that writes all events to
     * files within the given directory baseDir. In addition to that, the
     * writer creates summary-entries in an index-file denoted by indexFile.
     * 
     * @param baseDir The directory where to store the event-data files.
     * @param indexFile The file to which the index-entries are appended.
     * @throws IOException In case an IO-error occurs (file-permissions, etc.)
     */
    public ConcurrentAuditWriter( File baseDir, File indexFile )
    	throws IOException
    {
        dir = baseDir;
        if( ! dir.exists() )
            if( !dir.mkdirs() )
                throw new IOException("Cannot create directory "+dir.getAbsolutePath());

        if( ! (dir.isDirectory() && dir.canWrite() ) )
            throw new IOException("Cannot write to "+dir.getAbsolutePath());

        idx = indexFile;
        indexWriter = new PrintStream( new FileOutputStream( idx, true ) );
    }
    
    
    /**
     * Create a new audit-writer that writes events into sub-directories below the given 
     * directory base according to their date. The index-file is assumed to
     * be the file index within that directory.
     * 
     * @param baseDir The base directory where the audit-data files are stored.
     * @throws IOException In case base is not a directory or cannot be created or
     *         is not writable.
     */
    public ConcurrentAuditWriter(File baseDir)
        throws IOException
    {
    	this( baseDir, new File( baseDir.getAbsolutePath()+ "/index" ) );
    }
    
    
    /**
     * This method writes the given audit-event evt to a file, whose name is
     * deducted from the creation time of the event. The file is created relative to the
     * base-directory given at construction-time of this writer-instance.
     * 
     * Additionally a summary-entry will be created that is appended to the index-file.
     * 
     * @param evt The AuditEvent-instance to be written to disk.
     */
    public File write(AuditEvent evt) throws IOException
    {
        PrintStream wr = null;
        
        try {
            File f = getFileFor( evt );
            wr =  new PrintStream(new FileOutputStream( f , true));
            wr.print( evt.toString() );
            wr.flush();
            wr.close();
            count++;
            
            indexWriter.println( ConcurrentAuditWriter.createSummary( evt ) );
            return f;
        } catch (IOException e ){
            e.printStackTrace();
        }
        return null;
    }
    
    
    public File getFileFor( AuditEvent evt ) throws IOException {
        SimpleDateFormat ff = new SimpleDateFormat("yyyyMMdd-HHmmss-");
        File dir = getDirectoryFor( evt.getDate() );
        File f = new File( dir.getAbsolutePath() + "/" + ff.format( evt.getDate() ) + evt.getEventId() );
        int j = 0;
        while( f.exists() ){
            
            if( f.getAbsolutePath().endsWith("_"+(j-1) ) )
                f = new File( f.getAbsolutePath().replaceAll("_\\d+$", "_"+j) );
            else
                f = new File( f.getAbsolutePath() + "_" + j );
            j++;
        }

        return f;
    }
    
    public void writeEvent( AuditEvent evt ) throws IOException {
        write( evt );
    }
    

    /**
     * 
     * This method creates the file-name for the given date and also creates the
     * file itself and the corresponding subdirectories.
     * 
     * @param date The date of an event.
     * @return A file, denoting the absolute path to the events data file.
     * @throws IOException In case the file or any of the subdirectories cannot be created.
     */
    public File getDirectoryFor( Date date )
    throws IOException
    {
        File f = new File( dir.getAbsolutePath() + File.separator + DIR_FORMAT.format( date ) );

        if(! f.isDirectory() )
            if( ! f.mkdirs() )
                throw new IOException( "Could not create directory "+f.getAbsolutePath() );

        return f;
    }
    
    
    /**
     * 
     * This method creates a summary-string from the given audit-event. The summary is used
     * within the index-file.
     * 
     * @param evt The event to create the summary from.
     * @return A string, representing the summary of the event.
     */
    public static String createSummary( AuditEvent evt ){
		SimpleDateFormat fn = new SimpleDateFormat("/yyyyMMdd/yyyyMMdd-HHmm/yyyyMMdd-HHmmss-");
		String hash = MD5.md5( evt.toString().getBytes() );
		StringBuffer sum = new StringBuffer();
		
		sum.append( evt.get( ModSecurity.REQUEST_HEADERS + ":Host" ) );
		sum.append(" ");
		sum.append( evt.get( ModSecurity.REMOTE_ADDR) );
		sum.append(" - - ");
		sum.append( DATE_FORMAT.format( evt.getDate() ) );
		sum.append(" ");
		sum.append("\""+evt.get( ModSecurity.REQUEST_LINE )+"\"" );
		sum.append(" ");
		sum.append( evt.get( ModSecurity.RESPONSE_STATUS ) );  // status
		sum.append(" 0");  // bytes out
		sum.append(" \"-\"");  // referer
		sum.append(" \"-\"");  // user-agent
		sum.append(" " + evt.getEventId() );  // unique-id
		
		
		sum.append(" \"" + evt.getSessionId() + "\" ");           // session-id
		
		sum.append( fn.format( evt.getDate() ) );
		sum.append( evt.getEventId() );  // file-name
		sum.append( " 0 ");              // offset
		sum.append( evt.toString().length() );  // size
		
		sum.append( " md5:" + hash );
		
		return sum.toString();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy