org.xipki.audit.services.FileMacAuditService Maven / Gradle / Ivy
/*
*
* Copyright (c) 2013 - 2022 Lijun Liao
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xipki.audit.services;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xipki.password.PasswordResolver;
import org.xipki.util.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.Instant;
import java.util.Calendar;
import java.util.StringTokenizer;
import java.util.TimeZone;
/**
* File-based MAC protected audit service.
*
* @author Lijun Liao
* @since 6.0.0
*/
public class FileMacAuditService extends MacAuditService {
private static final Logger LOG = LoggerFactory.getLogger(FileMacAuditService.class);
public static final String KEY_FILE = "file";
private File logDir;
private String logFileNamePrefix;
private String logFileNameSuffix;
private long lastMsOfToday;
private OutputStreamWriter writer;
private Path integrityFilePath;
public FileMacAuditService() {
}
/*
If there is no previous log line, the previous id is 0, and previous tag is empty.
*/
@Override
protected void storeLog(Instant date, long thisId, int eventType, String levelText,
long previousId, String message, String thisTag) {
String logLine = formatDate(date) + DELIM + levelText + DELIM + eventType + DELIM + shardId +
DELIM + thisId + DELIM + previousId + DELIM + thisTag + DELIM + message;
long ms = date.toEpochMilli();
try {
if (ms > lastMsOfToday) {
Calendar now = Calendar.getInstance(TimeZone.getDefault());
now.setTimeInMillis(ms);
int yyyyMMddNow = DateUtil.getYyyyMMdd(now);
lastMsOfToday = DateUtil.getLastMsOfDay(now);
writer.close();
writer = buildWriter(yyyyMMddNow);
}
writer.write(logLine);
writer.write('\n');
} catch (Exception ex) {
LogUtil.error(LOG, ex);
}
}
@Override
protected void storeIntegrity(String integrityText) {
if (integrityText != null) {
try {
writer.flush();
Files.copy(new ByteArrayInputStream(integrityText.getBytes(StandardCharsets.UTF_8)),
integrityFilePath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
}
@Override
protected void doExtraInit(ConfPairs confPairs, PasswordResolver passwordResolver) {
String str = confPairs.value(KEY_FILE);
if (StringUtil.isBlank(str)) {
throw new IllegalArgumentException("property " + KEY_FILE + " not defined");
}
File logFile = new File(str).getAbsoluteFile();
this.logDir = logFile.getParentFile();
this.logDir.mkdirs();
String fileName = logFile.getName();
int idx = fileName.lastIndexOf('.');
logFileNameSuffix = idx == -1 ? "" : fileName.substring(idx);
String prefix = idx == -1 ? fileName : fileName.substring(0, idx);
if (shardId != 0) {
prefix += "-" + shardId;
}
this.logFileNamePrefix = prefix + "_";
// analyze the existing log files
Calendar now = Calendar.getInstance(TimeZone.getDefault());
int yyyyMMddNow = DateUtil.getYyyyMMdd(now);
this.lastMsOfToday = DateUtil.getLastMsOfDay(now);
File[] existingLogFiles = logDir.listFiles();
int latestYyyyMMdd = 0;
if (existingLogFiles != null) {
for (File f : existingLogFiles) {
String fName = f.getName();
if (!(f.isFile() && fName.startsWith(logFileNamePrefix) && fName.endsWith(logFileNameSuffix)
&& fName.length() == logFileNamePrefix.length() + 10 + logFileNameSuffix.length())) {
continue;
}
int startOffset = logFileNamePrefix.length();
String ds = f.getName().substring(startOffset, startOffset + 10);
try {
int yyyyMMdd = Integer.parseInt(ds.substring(0, 4) + ds.substring(5, 7) + ds.substring(8, 10));
if (yyyyMMdd > latestYyyyMMdd) {
latestYyyyMMdd = yyyyMMdd;
}
} catch (Exception ex) {
LOG.warn("could not parse name of file {}, ignore it", f.getAbsolutePath());
}
if (latestYyyyMMdd > yyyyMMddNow) {
throw new IllegalStateException("audit file " + f.getAbsolutePath()
+ " is generated after " + yyyyMMddNow + ", this is not allowed.");
}
}
}
this.integrityFilePath = new File(logDir,
this.logFileNamePrefix.substring(0, this.logFileNamePrefix.length() - 1) + ".integrity").toPath();
File integrityFile = integrityFilePath.toFile();
String integrityText;
try {
integrityText = integrityFile.exists() ? IoUtil.readLastNonBlankLine(integrityFile) : null;
} catch (IOException ex) {
throw new IllegalStateException("error reading " + integrityFile.getPath(), ex);
}
if (latestYyyyMMdd > 0) {
String lastLine;
File latestFile = new File(logDir, buildFilename(latestYyyyMMdd));
try {
lastLine = IoUtil.readLastNonBlankLine(latestFile);
} catch (IOException e) {
throw new IllegalStateException("error while reading " + latestFile.getPath());
}
StringTokenizer tokenizer = new StringTokenizer(lastLine, DELIM);
int count = tokenizer.countTokens();
String[] tokens = new String[count];
for (int i = 0; i < count; i++) {
tokens[i] = tokenizer.nextToken();
}
int previousId = Integer.parseInt(tokens[4]);
if (previousId < 1) {
throw new IllegalStateException("invalid previous id " + tokens[5]);
}
id.set(previousId);
previousTag = tokens[6];
}
verify(id.get(), previousTag, integrityText, confPairs);
this.writer = buildWriter(yyyyMMddNow);
}
private OutputStreamWriter buildWriter(int yyyyMMdd) {
File currentLogFile = new File(logDir, buildFilename(yyyyMMdd));
OutputStream fw;
try {
fw = new FileOutputStream(currentLogFile, true);
} catch (IOException ex) {
throw new IllegalStateException("error opening file " + currentLogFile.getPath());
}
return new OutputStreamWriter(fw);
}
private String buildFilename(int yyyyMMdd) {
int year = yyyyMMdd / 10000;
int month = yyyyMMdd % 10000 / 100;
int day = yyyyMMdd % 100;
String dateStr = year + "." + (month < 10 ? "0" + month : month) + "." + (day < 10 ? "0" + day : day);
return logFileNamePrefix + dateStr + logFileNameSuffix;
}
@Override
public void doClose() throws Exception {
if (writer != null) {
writer.flush();
writer.close();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy