/************************************************************************* * * * 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.certificates.certificatetransparency; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import org.apache.commons.lang.StringUtils; /** * This class is responsible for managing a list of CT logs. The CT logs backed by * this class can be grouped or reordered and there is functionality for adding * new CT logs or removing existing ones. This class does not load or store changes * in a persistent storage, and relies on its creator to load the CT logs from e.g. * a database or a file. * @version $Id: CtLogManager.java 28511 2018-03-18 21:24:03Z bastianf $ */ public class CtLogManager { private final List ctLogs; /** * Create a new CT log manager responsible for a list of logs specified. * @param ctLogs the logs managed by this CT log manager */ public CtLogManager(final List ctLogs) { this.ctLogs = ctLogs; } /** * Group the CT logs managed by this CT log manager by label. Returns a map * which maps a label to a list of all CT logs which has this label. * @return a mapping between labels and CT logs with this label */ public LinkedHashMap> getCtLogGroups() { final LinkedHashMap> logLogMap = new LinkedHashMap<>(); for (final CTLogInfo ctLog : ctLogs) { if (!logLogMap.containsKey(ctLog.getLabel())) { logLogMap.put(ctLog.getLabel(), new ArrayList()); } logLogMap.get(ctLog.getLabel()).add(ctLog); } return logLogMap; } /** * Returns all unique labels for the CT logs managed by this CT log manager. * @return a list of all unique labels */ public List getLabels() { List ret = new ArrayList(getCtLogGroups().keySet()); Collections.sort(ret, new Comparator() { @Override public int compare(String label1, String label2) { return label1.compareToIgnoreCase(label2); } }); return ret; } /** * Returns a list of all logs managed by this CT log manager. This list can be * persisted to a database and used to initialise an identical log manager. * @return a list of all CT logs */ public List getAllCtLogs() { return ctLogs; } /** * Get a list of CT logs labelled as specified. If no logs have the label specified, * an empty list will be returned. * @param label the label of the CT logs to retrieve * @return a list of CT logs matching the given label, never null */ public List getCtLogsByLabel(final String label) { final List ctLogGroup = getCtLogGroups().get(label); if (ctLogGroup == null) { return new ArrayList(); } return ctLogGroup; } /** * Moves the specified CT log up one step. This method does nothing if the CT log is already * on top or if the CT log group to which the CT log belongs only contains one log. * @throws IllegalArgumentException if the CT log given as argument is not managed by this CT log manager */ public void moveUp(final CTLogInfo ctLog) { final List ctLogGroup = getCtLogsByLabel(ctLog.getLabel()); if (!ctLogGroup.contains(ctLog)) { throw new IllegalArgumentException("The CT log " + ctLog.toString() + " is not managed by this CT log manager."); } if (ctLogGroup.size() == 1 || isOnTop(ctLog)) { return; } final CTLogInfo previousCtLog = ctLogGroup.get(ctLogGroup.indexOf(ctLog) - 1); Collections.swap(ctLogs, ctLogs.indexOf(ctLog), ctLogs.indexOf(previousCtLog)); } /** * Moves the specified CT log down one step. This method does nothing if the CT log is already * on the bottom or if the CT log group to which the CT log belongs only contains one log. * @throws IllegalArgumentException if the CT log given as argument is not managed by this CT log manager */ public void moveDown(final CTLogInfo ctLog) { final List ctLogGroup = getCtLogsByLabel(ctLog.getLabel()); if (!ctLogGroup.contains(ctLog)) { throw new IllegalArgumentException("The CT log " + ctLog.toString() + " is not managed by this CT log manager."); } if (ctLogGroup.size() == 1 || isOnBottom(ctLog)) { return; } final CTLogInfo nextCtLog = ctLogGroup.get(ctLogGroup.indexOf(ctLog) + 1); Collections.swap(ctLogs, ctLogs.indexOf(ctLog), ctLogs.indexOf(nextCtLog)); } /** * Add a new CT log to this CT log manager. This method will prevent duplicate logs from being added. * @param ctLog the CT log to add * @throws DuplicateCtLogException if the CT log to add is a duplicate according to {@link #canAdd(CTLogInfo)} */ public void addCtLog(final CTLogInfo ctLog) { if (!canAdd(ctLog)) { throw new DuplicateCtLogException("The CT log " + ctLog.toString() + " already exists in the log group '" + ctLog.getLabel() + "'."); } ctLogs.add(ctLog); } /** * Removes an existing CT log from this log manager. * @param ctLog the CT log to remove * @throws IllegalArgumentException if the CT log is not managed by this CT log manager */ public void removeCtLog(final CTLogInfo ctLog) { if (!ctLogs.contains(ctLog)) { throw new IllegalArgumentException("The CT log " + ctLog.toString() + " is not managed by this CT log manager."); } ctLogs.remove(ctLog); } /** * Determine whether the CT log given as input can be added to this CT log manager. A CT log cannot be added * if any of the following conditions hold for another CT log: *
    *
  • The other log has an ID identical to the new CT log
  • *
  • The other log is has an identical URL and label as the new CT log
  • *
* @param the new CT log to check * @return true if the CT log given as input can be added, false otherwise */ public boolean canAdd(final CTLogInfo ctLog) { for (CTLogInfo existing : ctLogs) { final boolean hasSameId = existing.getLogId() == ctLog.getLogId(); final boolean urlExistsInCtLogGroup = StringUtils.equals(existing.getUrl(), ctLog.getUrl()) && StringUtils.equals(existing.getLabel(), ctLog.getLabel()); if (hasSameId || urlExistsInCtLogGroup) { return false; } } return true; } /** * Determine whether the CT log given as input is on the top in its CT log group. * @param ctLog the CT log to check * @return true iff the CT log is on the top in its CT log group */ public boolean isOnTop(final CTLogInfo ctLog) { final List ctLogGroup = getCtLogsByLabel(ctLog.getLabel()); return !ctLogGroup.isEmpty() && ctLogGroup.get(0).equals(ctLog); } /** * Determine whether the CT log given as input is on the bottom in its CT log group. * @param ctLog the CT log to check * @return true iff the CT log is on the bottom in its CT log group */ public boolean isOnBottom(final CTLogInfo ctLog) { final List ctLogGroup = getCtLogsByLabel(ctLog.getLabel()); return !ctLogGroup.isEmpty() && ctLogGroup.get(ctLogGroup.size() - 1).equals(ctLog); } /** * Rename a label. This will effectively set the label of CT logs to the * new label for every CT log with the old label. * @param oldLabel the label to change * @param newLabel the new label to set */ public void renameLabel(final String oldLabel, final String newLabel) { for (CTLogInfo ctLog : ctLogs) { if (oldLabel.equals(ctLog.getLabel())) { ctLog.setLabel(newLabel); } } } /** * Returns the string representation of this object containing * the CT logs currently managed by this CT log manager. */ @Override public String toString() { return "CT logs: " + getAllCtLogs().toString(); } }