001package co.codewizards.cloudstore.rest.server.auth; 002 003import java.util.Arrays; 004import java.util.Comparator; 005import java.util.Date; 006import java.util.HashMap; 007import java.util.Map; 008import java.util.SortedSet; 009import java.util.Timer; 010import java.util.TimerTask; 011import java.util.TreeSet; 012import java.util.UUID; 013 014import co.codewizards.cloudstore.core.auth.AuthToken; 015import co.codewizards.cloudstore.core.config.ConfigImpl; 016import co.codewizards.cloudstore.core.dto.DateTime; 017import co.codewizards.cloudstore.core.util.AssertUtil; 018import co.codewizards.cloudstore.core.util.PasswordUtil; 019 020public class TransientRepoPasswordManager { 021 022 private static final int DEFAULT_VALIDITIY_PERIOD = 60 * 60 * 1000; 023 private static final int DEFAULT_RENEWAL_PERIOD = 30 * 60 * 1000; 024 private static final int DEFAULT_EARLY_RENEWAL_PERIOD = 15 * 60 * 1000; 025 private static final int DEFAULT_EXPIRY_TIMER_PERIOD = 60 * 1000; 026 027 public static final String CONFIG_KEY_VALIDITIY_PERIOD = "transientRepoPassword.validityPeriod"; 028 public static final String CONFIG_KEY_RENEWAL_PERIOD = "transientRepoPassword.renewalPeriod"; 029 public static final String CONFIG_KEY_EARLY_RENEWAL_PERIOD = "transientRepoPassword.earlyRenewalPeriod"; 030 public static final String CONFIG_KEY_EXPIRY_TIMER_PERIOD = "transientRepoPassword.expiryTimerPeriod"; 031 032 private int validityPeriod = Integer.MIN_VALUE; 033 private int renewalPeriod = Integer.MIN_VALUE; 034 private int earlyRenewalPeriod = Integer.MIN_VALUE; 035 private int expiryTimerPeriod = Integer.MIN_VALUE; 036 037 private static class TransientRepoPasswordManagerHolder { 038 public static final TransientRepoPasswordManager instance = new TransientRepoPasswordManager(); 039 } 040 041 protected TransientRepoPasswordManager() { } 042 043 public static TransientRepoPasswordManager getInstance() { 044 return TransientRepoPasswordManagerHolder.instance; 045 } 046 047 private final Map<UUID, Map<UUID, SortedSet<TransientRepoPassword>>> serverRepositoryId2ClientRepositoryId2AuthRepoPasswordSet = new HashMap<UUID, Map<UUID,SortedSet<TransientRepoPassword>>>(); 048 private final SortedSet<TransientRepoPassword> transientRepoPasswords = new TreeSet<TransientRepoPassword>(newestFirstAuthRepoPasswordComparator); 049 050 private final Timer timer = new Timer(); 051 private final TimerTask removeExpiredAuthRepoPasswordsTimerTask = new TimerTask() { 052 @Override 053 public void run() { 054 removeExpiredAuthRepoPasswords(); 055 } 056 }; 057 { 058 timer.schedule(removeExpiredAuthRepoPasswordsTimerTask, getExpiryTimerPeriod(), getExpiryTimerPeriod()); 059 } 060 061 public synchronized TransientRepoPassword getCurrentAuthRepoPassword(final UUID serverRepositoryId, final UUID clientRepositoryId) { 062 AssertUtil.assertNotNull(serverRepositoryId, "serverRepositoryId"); 063 AssertUtil.assertNotNull(clientRepositoryId, "clientRepositoryId"); 064 065 Map<UUID, SortedSet<TransientRepoPassword>> clientRepositoryId2AuthRepoPasswordSet = serverRepositoryId2ClientRepositoryId2AuthRepoPasswordSet.get(serverRepositoryId); 066 if (clientRepositoryId2AuthRepoPasswordSet == null) { 067 clientRepositoryId2AuthRepoPasswordSet = new HashMap<UUID, SortedSet<TransientRepoPassword>>(); 068 serverRepositoryId2ClientRepositoryId2AuthRepoPasswordSet.put(serverRepositoryId, clientRepositoryId2AuthRepoPasswordSet); 069 } 070 071 SortedSet<TransientRepoPassword> authRepoPasswordSet = clientRepositoryId2AuthRepoPasswordSet.get(clientRepositoryId); 072 if (authRepoPasswordSet == null) { 073 authRepoPasswordSet = new TreeSet<TransientRepoPassword>(newestFirstAuthRepoPasswordComparator); 074 clientRepositoryId2AuthRepoPasswordSet.put(clientRepositoryId, authRepoPasswordSet); 075 } 076 077 TransientRepoPassword transientRepoPassword = authRepoPasswordSet.isEmpty() ? null : authRepoPasswordSet.first(); 078 if (transientRepoPassword != null && isAfterRenewalDateOrInEarlyRenewalPeriod(transientRepoPassword)) 079 transientRepoPassword = null; 080 081 if (transientRepoPassword == null) { 082 transientRepoPassword = new TransientRepoPassword(serverRepositoryId, clientRepositoryId, createAuthToken()); 083 authRepoPasswordSet.add(transientRepoPassword); 084 transientRepoPasswords.add(transientRepoPassword); 085 } 086 return transientRepoPassword; 087 } 088 089 public synchronized boolean isPasswordValid(final UUID serverRepositoryId, final UUID clientRepositoryId, final char[] password) { 090 AssertUtil.assertNotNull(serverRepositoryId, "serverRepositoryId"); 091 AssertUtil.assertNotNull(clientRepositoryId, "clientRepositoryId"); 092 AssertUtil.assertNotNull(password, "password"); 093 final Map<UUID, SortedSet<TransientRepoPassword>> clientRepositoryId2AuthRepoPasswordSet = serverRepositoryId2ClientRepositoryId2AuthRepoPasswordSet.get(serverRepositoryId); 094 if (clientRepositoryId2AuthRepoPasswordSet == null) 095 return false; 096 097 final SortedSet<TransientRepoPassword> authRepoPasswordSet = clientRepositoryId2AuthRepoPasswordSet.get(clientRepositoryId); 098 if (authRepoPasswordSet == null) 099 return false; 100 101 for (final TransientRepoPassword transientRepoPassword : authRepoPasswordSet) { 102 if (isExpired(transientRepoPassword)) // newest first => first expired means all following expired, too! 103 return false; 104 105 if (Arrays.equals(password, transientRepoPassword.getPassword())) 106 return true; 107 } 108 return false; 109 } 110 111 private synchronized void removeExpiredAuthRepoPasswords() { 112 while (!transientRepoPasswords.isEmpty()) { 113 final TransientRepoPassword oldestAuthRepoPassword = transientRepoPasswords.last(); 114 if (!isExpired(oldestAuthRepoPassword)) // newest first => last not yet expired means all previous not yet expired, either 115 break; 116 117 transientRepoPasswords.remove(oldestAuthRepoPassword); 118 final UUID serverRepositoryId = oldestAuthRepoPassword.getServerRepositoryId(); 119 final UUID clientRepositoryId = oldestAuthRepoPassword.getClientRepositoryId(); 120 121 final Map<UUID, SortedSet<TransientRepoPassword>> clientRepositoryId2AuthRepoPasswordSet = serverRepositoryId2ClientRepositoryId2AuthRepoPasswordSet.get(serverRepositoryId); 122 AssertUtil.assertNotNull(clientRepositoryId2AuthRepoPasswordSet, "clientRepositoryId2AuthRepoPasswordSet"); 123 124 final SortedSet<TransientRepoPassword> authRepoPasswordSet = clientRepositoryId2AuthRepoPasswordSet.get(clientRepositoryId); 125 AssertUtil.assertNotNull(authRepoPasswordSet, "authRepoPasswordSet"); 126 127 authRepoPasswordSet.remove(oldestAuthRepoPassword); 128 129 if (authRepoPasswordSet.isEmpty()) 130 clientRepositoryId2AuthRepoPasswordSet.remove(clientRepositoryId); 131 132 if (clientRepositoryId2AuthRepoPasswordSet.isEmpty()) 133 serverRepositoryId2ClientRepositoryId2AuthRepoPasswordSet.remove(serverRepositoryId); 134 } 135 } 136 137 protected int getValidityPeriod() { 138 if (validityPeriod == Integer.MIN_VALUE) { 139 validityPeriod = ConfigImpl.getInstance().getPropertyAsInt( 140 CONFIG_KEY_VALIDITIY_PERIOD, DEFAULT_VALIDITIY_PERIOD); 141 } 142 return validityPeriod; 143 } 144 145 protected int getRenewalPeriod() { 146 if (renewalPeriod == Integer.MIN_VALUE) { 147 renewalPeriod = ConfigImpl.getInstance().getPropertyAsInt( 148 CONFIG_KEY_RENEWAL_PERIOD, DEFAULT_RENEWAL_PERIOD); 149 } 150 return renewalPeriod; 151 } 152 153 protected int getEarlyRenewalPeriod() { 154 if (earlyRenewalPeriod == Integer.MIN_VALUE) { 155 earlyRenewalPeriod = ConfigImpl.getInstance().getPropertyAsInt( 156 CONFIG_KEY_EARLY_RENEWAL_PERIOD, DEFAULT_EARLY_RENEWAL_PERIOD); 157 } 158 return earlyRenewalPeriod; 159 } 160 161 protected int getExpiryTimerPeriod() { 162 if (expiryTimerPeriod == Integer.MIN_VALUE) { 163 expiryTimerPeriod = ConfigImpl.getInstance().getPropertyAsInt( 164 CONFIG_KEY_EXPIRY_TIMER_PERIOD, DEFAULT_EXPIRY_TIMER_PERIOD); 165 } 166 return expiryTimerPeriod; 167 } 168 169 private static final Comparator<TransientRepoPassword> newestFirstAuthRepoPasswordComparator = new Comparator<TransientRepoPassword>() { 170 @Override 171 public int compare(final TransientRepoPassword o1, final TransientRepoPassword o2) { 172 final Date expiryDate1 = o1.getAuthToken().getExpiryDateTime().toDate(); 173 final Date expiryDate2 = o2.getAuthToken().getExpiryDateTime().toDate(); 174 175 if (expiryDate1.before(expiryDate2)) 176 return +1; 177 178 if (expiryDate1.after(expiryDate2)) 179 return -1; 180 181 int result = o1.getServerRepositoryId().compareTo(o2.getServerRepositoryId()); 182 if (result != 0) 183 return result; 184 185 result = o1.getClientRepositoryId().compareTo(o2.getClientRepositoryId()); 186 return result; 187 } 188 }; 189 190 private boolean isAfterRenewalDateOrInEarlyRenewalPeriod(final TransientRepoPassword transientRepoPassword) { 191 AssertUtil.assertNotNull(transientRepoPassword, "authRepoPassword"); 192 return System.currentTimeMillis() + getEarlyRenewalPeriod() > transientRepoPassword.getAuthToken().getRenewalDateTime().getMillis(); 193 } 194 195 private boolean isExpired(final TransientRepoPassword transientRepoPassword) { 196 AssertUtil.assertNotNull(transientRepoPassword, "authRepoPassword"); 197 return System.currentTimeMillis() > transientRepoPassword.getAuthToken().getExpiryDateTime().getMillis(); 198 } 199 200 private AuthToken createAuthToken() { 201 final AuthToken authToken = new AuthToken(); 202 final Date expiryDate = new Date(System.currentTimeMillis() + getValidityPeriod()); 203 final Date renewalDate = new Date(System.currentTimeMillis() + getRenewalPeriod()); 204 authToken.setExpiryDateTime(new DateTime(expiryDate)); 205 authToken.setRenewalDateTime(new DateTime(renewalDate)); 206 authToken.setPassword(new String(PasswordUtil.createRandomPassword(40))); 207 authToken.makeUnmodifiable(); 208 return authToken; 209 } 210 211}