package co.codewizards.cloudstore.rest.server.auth; import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.SortedSet; import java.util.Timer; import java.util.TimerTask; import java.util.TreeSet; import java.util.UUID; import co.codewizards.cloudstore.core.auth.AuthToken; import co.codewizards.cloudstore.core.config.ConfigImpl; import co.codewizards.cloudstore.core.dto.DateTime; import co.codewizards.cloudstore.core.util.AssertUtil; import co.codewizards.cloudstore.core.util.PasswordUtil; public class TransientRepoPasswordManager { private static final int DEFAULT_VALIDITIY_PERIOD = 60 * 60 * 1000; private static final int DEFAULT_RENEWAL_PERIOD = 30 * 60 * 1000; private static final int DEFAULT_EARLY_RENEWAL_PERIOD = 15 * 60 * 1000; private static final int DEFAULT_EXPIRY_TIMER_PERIOD = 60 * 1000; public static final String CONFIG_KEY_VALIDITIY_PERIOD = "transientRepoPassword.validityPeriod"; public static final String CONFIG_KEY_RENEWAL_PERIOD = "transientRepoPassword.renewalPeriod"; public static final String CONFIG_KEY_EARLY_RENEWAL_PERIOD = "transientRepoPassword.earlyRenewalPeriod"; public static final String CONFIG_KEY_EXPIRY_TIMER_PERIOD = "transientRepoPassword.expiryTimerPeriod"; private int validityPeriod = Integer.MIN_VALUE; private int renewalPeriod = Integer.MIN_VALUE; private int earlyRenewalPeriod = Integer.MIN_VALUE; private int expiryTimerPeriod = Integer.MIN_VALUE; private static class TransientRepoPasswordManagerHolder { public static final TransientRepoPasswordManager instance = new TransientRepoPasswordManager(); } protected TransientRepoPasswordManager() { } public static TransientRepoPasswordManager getInstance() { return TransientRepoPasswordManagerHolder.instance; } private final Map<UUID, Map<UUID, SortedSet<TransientRepoPassword>>> serverRepositoryId2ClientRepositoryId2AuthRepoPasswordSet = new HashMap<UUID, Map<UUID,SortedSet<TransientRepoPassword>>>(); private final SortedSet<TransientRepoPassword> transientRepoPasswords = new TreeSet<TransientRepoPassword>(newestFirstAuthRepoPasswordComparator); private final Timer timer = new Timer(); private final TimerTask removeExpiredAuthRepoPasswordsTimerTask = new TimerTask() { @Override public void run() { removeExpiredAuthRepoPasswords(); } }; { timer.schedule(removeExpiredAuthRepoPasswordsTimerTask, getExpiryTimerPeriod(), getExpiryTimerPeriod()); } public synchronized TransientRepoPassword getCurrentAuthRepoPassword(final UUID serverRepositoryId, final UUID clientRepositoryId) { AssertUtil.assertNotNull(serverRepositoryId, "serverRepositoryId"); AssertUtil.assertNotNull(clientRepositoryId, "clientRepositoryId"); Map<UUID, SortedSet<TransientRepoPassword>> clientRepositoryId2AuthRepoPasswordSet = serverRepositoryId2ClientRepositoryId2AuthRepoPasswordSet.get(serverRepositoryId); if (clientRepositoryId2AuthRepoPasswordSet == null) { clientRepositoryId2AuthRepoPasswordSet = new HashMap<UUID, SortedSet<TransientRepoPassword>>(); serverRepositoryId2ClientRepositoryId2AuthRepoPasswordSet.put(serverRepositoryId, clientRepositoryId2AuthRepoPasswordSet); } SortedSet<TransientRepoPassword> authRepoPasswordSet = clientRepositoryId2AuthRepoPasswordSet.get(clientRepositoryId); if (authRepoPasswordSet == null) { authRepoPasswordSet = new TreeSet<TransientRepoPassword>(newestFirstAuthRepoPasswordComparator); clientRepositoryId2AuthRepoPasswordSet.put(clientRepositoryId, authRepoPasswordSet); } TransientRepoPassword transientRepoPassword = authRepoPasswordSet.isEmpty() ? null : authRepoPasswordSet.first(); if (transientRepoPassword != null && isAfterRenewalDateOrInEarlyRenewalPeriod(transientRepoPassword)) transientRepoPassword = null; if (transientRepoPassword == null) { transientRepoPassword = new TransientRepoPassword(serverRepositoryId, clientRepositoryId, createAuthToken()); authRepoPasswordSet.add(transientRepoPassword); transientRepoPasswords.add(transientRepoPassword); } return transientRepoPassword; } public synchronized boolean isPasswordValid(final UUID serverRepositoryId, final UUID clientRepositoryId, final char[] password) { AssertUtil.assertNotNull(serverRepositoryId, "serverRepositoryId"); AssertUtil.assertNotNull(clientRepositoryId, "clientRepositoryId"); AssertUtil.assertNotNull(password, "password"); final Map<UUID, SortedSet<TransientRepoPassword>> clientRepositoryId2AuthRepoPasswordSet = serverRepositoryId2ClientRepositoryId2AuthRepoPasswordSet.get(serverRepositoryId); if (clientRepositoryId2AuthRepoPasswordSet == null) return false; final SortedSet<TransientRepoPassword> authRepoPasswordSet = clientRepositoryId2AuthRepoPasswordSet.get(clientRepositoryId); if (authRepoPasswordSet == null) return false; for (final TransientRepoPassword transientRepoPassword : authRepoPasswordSet) { if (isExpired(transientRepoPassword)) // newest first => first expired means all following expired, too! return false; if (Arrays.equals(password, transientRepoPassword.getPassword())) return true; } return false; } private synchronized void removeExpiredAuthRepoPasswords() { while (!transientRepoPasswords.isEmpty()) { final TransientRepoPassword oldestAuthRepoPassword = transientRepoPasswords.last(); if (!isExpired(oldestAuthRepoPassword)) // newest first => last not yet expired means all previous not yet expired, either break; transientRepoPasswords.remove(oldestAuthRepoPassword); final UUID serverRepositoryId = oldestAuthRepoPassword.getServerRepositoryId(); final UUID clientRepositoryId = oldestAuthRepoPassword.getClientRepositoryId(); final Map<UUID, SortedSet<TransientRepoPassword>> clientRepositoryId2AuthRepoPasswordSet = serverRepositoryId2ClientRepositoryId2AuthRepoPasswordSet.get(serverRepositoryId); AssertUtil.assertNotNull(clientRepositoryId2AuthRepoPasswordSet, "clientRepositoryId2AuthRepoPasswordSet"); final SortedSet<TransientRepoPassword> authRepoPasswordSet = clientRepositoryId2AuthRepoPasswordSet.get(clientRepositoryId); AssertUtil.assertNotNull(authRepoPasswordSet, "authRepoPasswordSet"); authRepoPasswordSet.remove(oldestAuthRepoPassword); if (authRepoPasswordSet.isEmpty()) clientRepositoryId2AuthRepoPasswordSet.remove(clientRepositoryId); if (clientRepositoryId2AuthRepoPasswordSet.isEmpty()) serverRepositoryId2ClientRepositoryId2AuthRepoPasswordSet.remove(serverRepositoryId); } } protected int getValidityPeriod() { if (validityPeriod == Integer.MIN_VALUE) { validityPeriod = ConfigImpl.getInstance().getPropertyAsInt( CONFIG_KEY_VALIDITIY_PERIOD, DEFAULT_VALIDITIY_PERIOD); } return validityPeriod; } protected int getRenewalPeriod() { if (renewalPeriod == Integer.MIN_VALUE) { renewalPeriod = ConfigImpl.getInstance().getPropertyAsInt( CONFIG_KEY_RENEWAL_PERIOD, DEFAULT_RENEWAL_PERIOD); } return renewalPeriod; } protected int getEarlyRenewalPeriod() { if (earlyRenewalPeriod == Integer.MIN_VALUE) { earlyRenewalPeriod = ConfigImpl.getInstance().getPropertyAsInt( CONFIG_KEY_EARLY_RENEWAL_PERIOD, DEFAULT_EARLY_RENEWAL_PERIOD); } return earlyRenewalPeriod; } protected int getExpiryTimerPeriod() { if (expiryTimerPeriod == Integer.MIN_VALUE) { expiryTimerPeriod = ConfigImpl.getInstance().getPropertyAsInt( CONFIG_KEY_EXPIRY_TIMER_PERIOD, DEFAULT_EXPIRY_TIMER_PERIOD); } return expiryTimerPeriod; } private static final Comparator<TransientRepoPassword> newestFirstAuthRepoPasswordComparator = new Comparator<TransientRepoPassword>() { @Override public int compare(final TransientRepoPassword o1, final TransientRepoPassword o2) { final Date expiryDate1 = o1.getAuthToken().getExpiryDateTime().toDate(); final Date expiryDate2 = o2.getAuthToken().getExpiryDateTime().toDate(); if (expiryDate1.before(expiryDate2)) return +1; if (expiryDate1.after(expiryDate2)) return -1; int result = o1.getServerRepositoryId().compareTo(o2.getServerRepositoryId()); if (result != 0) return result; result = o1.getClientRepositoryId().compareTo(o2.getClientRepositoryId()); return result; } }; private boolean isAfterRenewalDateOrInEarlyRenewalPeriod(final TransientRepoPassword transientRepoPassword) { AssertUtil.assertNotNull(transientRepoPassword, "authRepoPassword"); return System.currentTimeMillis() + getEarlyRenewalPeriod() > transientRepoPassword.getAuthToken().getRenewalDateTime().getMillis(); } private boolean isExpired(final TransientRepoPassword transientRepoPassword) { AssertUtil.assertNotNull(transientRepoPassword, "authRepoPassword"); return System.currentTimeMillis() > transientRepoPassword.getAuthToken().getExpiryDateTime().getMillis(); } private AuthToken createAuthToken() { final AuthToken authToken = new AuthToken(); final Date expiryDate = new Date(System.currentTimeMillis() + getValidityPeriod()); final Date renewalDate = new Date(System.currentTimeMillis() + getRenewalPeriod()); authToken.setExpiryDateTime(new DateTime(expiryDate)); authToken.setRenewalDateTime(new DateTime(renewalDate)); authToken.setPassword(new String(PasswordUtil.createRandomPassword(40))); authToken.makeUnmodifiable(); return authToken; } }