org.cesecore.audit.impl.queued.entity.LogManagementData.java Source code

Java tutorial

Introduction

Here is the source code for org.cesecore.audit.impl.queued.entity.LogManagementData.java

Source

/*************************************************************************
 *                                                                       *
 *  CESeCore: CE Security Core                                           *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/
package org.cesecore.audit.impl.queued.entity;

import java.io.IOException;
import java.io.Serializable;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.PostLoad;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Query;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.apache.commons.lang.time.FastDateFormat;
import org.apache.log4j.Logger;
import org.cesecore.audit.audit.AuditExporter;
import org.cesecore.audit.impl.queued.AuditLogSigningException;
import org.cesecore.audit.impl.queued.management.InvalidFrequencyException;
import org.cesecore.audit.impl.queued.management.LogManagementException;
import org.cesecore.dbprotection.ProtectedData;
import org.cesecore.dbprotection.ProtectionStringBuilder;
import org.cesecore.keys.token.CryptoToken;
import org.cesecore.keys.token.CryptoTokenAuthenticationFailedException;
import org.cesecore.keys.token.CryptoTokenFactory;
import org.cesecore.keys.token.CryptoTokenOfflineException;
import org.cesecore.util.JsonSerializer;
import org.cesecore.util.QueryResultWrapper;
import org.cesecore.util.Validator;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.hibernate.annotations.Cache;

/**
 * 
 * Abstract class to handle audit log configuration.
 * 
 * @version $Id$
 * 
 */
@Entity
@Table(name = "LogManagementData")
@NamedQueries({
        @NamedQuery(name = "LogManagementData.CURRENT", query = "SELECT a FROM LogManagementData a WHERE a.id = (SELECT MAX(b.id) FROM LogManagementData b)") })
@Cache(region = "LogManagementData", usage = org.hibernate.annotations.CacheConcurrencyStrategy.READ_WRITE)
public abstract class LogManagementData extends ProtectedData implements Serializable {

    private static final long serialVersionUID = -4783385097501386477L;
    private static final Logger log = Logger.getLogger(LogManagementData.class);

    public static final String TABLE_NAME = "LogManagementData";

    public static final String JDBC_LOG_MANAGEMENT_IN_BY_ID = "select id, timestamp, signMode, frequency, details "
            + "from LogManagementData where id in (select config_id from AuditLogData where timeStamp <= ?) order by id asc";

    public static final String FIELD_ID = "id";
    public static final String FIELD_TIMESTAMP = "timestamp";
    public static final String FIELD_SIGNMODE = "signMode";
    public static final String FIELD_FREQUENCY = "frequency";
    public static final String FIELD_DETAILS = "details";

    private static final FastDateFormat ISO8601_DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss");
    private static final long MIN_FREQUENCY = 100l; //milliseconds

    private Long id;
    private Long timestamp;
    private String signMode;
    private Long frequency;
    // FIXME: In a near FUTURE this should be nullable = false
    private String details;
    private List<AuditLogData> logs;
    private AuditLogCryptoTokenConfigData tokenConfig;
    private int rowVersion = 0;
    private String rowProtection;
    private CryptoToken cryptoToken;

    public Long getId() {
        return id;
    }

    public void setId(final Long id) {
        this.id = id;
    }

    public Long getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(final Long timestamp) {
        this.timestamp = timestamp;
    }

    public String getSignMode() {
        return signMode;
    }

    public void setSignMode(final String signMode) {
        this.signMode = signMode;
    }

    public long getFrequency() {
        return frequency;
    }

    public void setFrequency(final Long frequency) {
        this.frequency = frequency;
    }

    public String getDetails() {
        return details;
    }

    public void setDetails(final String details) {
        this.details = details;
    }

    @Transient
    @SuppressWarnings("unchecked")
    protected Map<String, Object> getMapDetails() {
        Map<String, Object> result = new LinkedHashMap<String, Object>();
        try {
            if (this.details != null)
                result = (Map<String, Object>) JsonSerializer.fromJSON(this.details);
        } catch (final JsonParseException e) {
            log.error(e.getMessage(), e);
        } catch (final JsonMappingException e) {
            log.error(e.getMessage(), e);
        } catch (final IOException e) {
            log.error(e.getMessage(), e);
        }
        return result;
    }

    @Transient
    protected void setMapDetails(final Map<String, Object> details) {
        try {
            this.details = JsonSerializer.toJSON(details);
        } catch (final JsonGenerationException e) {
            log.error(e.getMessage(), e);
        } catch (final JsonMappingException e) {
            log.error(e.getMessage(), e);
        } catch (final IOException e) {
            log.error(e.getMessage(), e);
        }
    }

    @Transient
    public String getKeyLabel() {
        final Map<String, Object> details = getMapDetails();
        return (String) details.get("label");
    }

    @Transient
    public void setKeyLabel(final String keyLabel) {
        final Map<String, Object> details = getMapDetails();
        details.put("label", keyLabel);
        setMapDetails(details);
    }

    @Transient
    public String getAlgorithm() {
        final Map<String, Object> details = getMapDetails();
        return (String) details.get("algorithm");
    }

    @Transient
    public void setAlgorithm(final String algorithm) {
        final Map<String, Object> details = getMapDetails();
        details.put("algorithm", algorithm);
        setMapDetails(details);
    }

    public List<AuditLogData> getLogs() {
        return logs;
    }

    public void setLogs(final List<AuditLogData> logs) {
        this.logs = logs;
    }

    /**
     * Gets the auditLogCryptoTokenConfig for this instance.
     * 
     * @return The auditLogCryptoTokenConfig.
     */
    public AuditLogCryptoTokenConfigData getTokenConfig() {
        return this.tokenConfig;
    }

    /**
     * Sets the auditLogCryptoTokenConfig for this instance.
     * 
     * @param auditLogCryptoTokenConfig
     *            The auditLogCryptoTokenConfig.
     */
    public void setTokenConfig(final AuditLogCryptoTokenConfigData tokenConfig) {
        this.tokenConfig = tokenConfig;
    }

    /**
     * Gets the rowVersion for this instance.
     * 
     * @return The rowVersion.
     */
    public int getRowVersion() {
        return this.rowVersion;
    }

    /**
     * Sets the rowVersion for this instance.
     * 
     * @param rowVersion
     *            The rowVersion.
     */
    public void setRowVersion(final int rowVersion) {
        this.rowVersion = rowVersion;
    }

    /**
     * {@inheritDoc}
     * 
     * @see ProtectedData#getRowProtection()
     */
    public String getRowProtection() {
        return this.rowProtection;
    }

    /**
     * {@inheritDoc}
     * 
     * @see ProtectedData#setRowProtection(String)
     */
    public void setRowProtection(final String rowProtection) {
        this.rowProtection = rowProtection;
    }

    public void save(final EntityManager em) {
        em.persist(this);
    }

    public static LogManagementData getCurrentConfiguration(final EntityManager em) {
        final Query query = em.createNamedQuery("LogManagementData.CURRENT").setHint("org.hibernate.cacheable",
                true);
        return QueryResultWrapper.getSingleResult(query);
    }

    public static LogManagementData findById(final EntityManager em, final Long id) {
        return em.find(LogManagementData.class, id);
    }

    @Transient
    public static void toExport(final AuditExporter auditExporter, final Long id, final Long timestamp,
            final String signMode, final Long frequency, final String details)
            throws JsonGenerationException, IOException {
        auditExporter.writeStartObject();
        auditExporter.writeField(FIELD_ID, id);
        auditExporter.writeField(FIELD_TIMESTAMP, timestamp);
        auditExporter.writeField(FIELD_SIGNMODE, signMode);
        auditExporter.writeField(FIELD_FREQUENCY, frequency);
        auditExporter.writeField(FIELD_DETAILS, details);
        auditExporter.writeEndObject();
    }

    /**
     * Will be used to validate the current instance. i.e if it as the proper set of attributes
     * In case anything is wrong this method will throw an exception
     * 
     * @throws LogManagementException
     */
    public void validate() throws LogManagementException {
        //test required fields
        if (Validator.notNull(getSignModeDescriptor(), frequency, tokenConfig, details) != Validator.Result.VALID) {
            throw new LogManagementException("missing required field");
        }
        //test frequency
        if (Validator.isEqual(0l, frequency) != Validator.Result.VALID) {
            if (Validator.inRange(0l, MIN_FREQUENCY, frequency) == Validator.Result.VALID) {
                throw new InvalidFrequencyException("Frequency is not in a valid interval");
            }
        }
    }

    public abstract LogManagementData metaClone();

    //
    // Start Database integrity protection methods
    //
    @Transient
    @Override
    protected String getTableName() {
        return TABLE_NAME;
    }

    @Transient
    private String getSignModeDescriptor() {
        final DiscriminatorValue dv = this.getClass().getAnnotation(DiscriminatorValue.class);
        return dv.value();
    }

    @Transient
    public CryptoToken getCryptoToken() {
        return cryptoToken;
    }

    protected void initCryptoToken() throws CryptoTokenOfflineException, CryptoTokenAuthenticationFailedException {
        final String tokenClassname = getTokenConfig().getClassname();
        final Properties tokenProperties = getTokenConfig().getProperties();
        final byte[] tokenData = getTokenConfig().getTokenData();
        cryptoToken = CryptoTokenFactory.createCryptoToken(tokenClassname, tokenProperties, tokenData, 1);
        cryptoToken.activate(((String) tokenProperties.get(CryptoToken.AUTOACTIVATE_PIN_PROPERTY)).toCharArray());
    }

    public byte[] sign(final EntityManager em, final byte[] data) throws AuditLogSigningException {
        try {
            final CryptoToken token = getCryptoToken();
            if (token == null) {
                throw new AuditLogSigningException("crypto token was not initialized");
            }
            return sign(token, data);
        } catch (final Exception e) {
            throw new AuditLogSigningException(e.getMessage(), e);
        }
    }

    public abstract byte[] sign(final CryptoToken cryptoToken, final byte[] data) throws AuditLogSigningException;

    @Transient
    @Override
    protected String getProtectString(final int version) {
        final ProtectionStringBuilder build = new ProtectionStringBuilder(1200);
        // What is important to protect here is the data that we define, id,
        // name and certificate profile data
        // rowVersion is automatically updated by JPA, so it's not important, it
        // is only used for optimistic locking
        build.append(getSignModeDescriptor()).append(ISO8601_DATE_FORMAT.format(getTimestamp()))
                .append(getFrequency()).append(getDetails()).append(getTokenConfig().getId());
        if (log.isDebugEnabled()) {
            // Some profiling
            if (build.length() > 1200) {
                log.debug("LogManagementData.getProtectString gives size: " + build.length());
            }
        }
        return build.toString();
    }

    @Transient
    @Override
    protected int getProtectVersion() {
        return 1;
    }

    /**
     * this method should have the @PrePersist ... however JPA enforces that
     * only one method can have this annotation
     * 
     * Since we have to do other stuff @see protectDataAndGenerateTimestamp()
     */
    @PreUpdate
    @Transient
    @Override
    protected void protectData() {
        super.protectData();
    }

    @Transient
    protected void protectDataAndGenerateTimestamp() {
        this.timestamp = new Date().getTime();
        protectData();
    }

    @Override
    @Transient
    protected String getRowId() {
        return String.valueOf(getId());
    }

    protected abstract void prePersistWork() throws Exception;

    @PrePersist
    @Transient
    protected void prePersist() throws Exception {
        if (getTokenConfig() != null) {
            initCryptoToken();
        }
        prePersistWork();
        protectDataAndGenerateTimestamp();
    }

    protected abstract void postLoadWork() throws Exception;

    @PostLoad
    @Transient
    protected void postLoad() throws Exception {
        if (getTokenConfig() != null) {
            initCryptoToken();
        }
        postLoadWork();
        super.verifyData();
    }

    @Override
    public String toString() {
        return "LogManagementData [id=" + id + ", timestamp=" + timestamp + ", signMode=" + signMode
                + ", frequency=" + frequency + ", details=" + details + ", tokenConfig=" + tokenConfig
                + ", rowVersion=" + rowVersion + ", rowProtection=" + rowProtection + ", cryptoToken=" + cryptoToken
                + "]";
    }
}