001package co.codewizards.cloudstore.rest.server.auth; 002 003import static co.codewizards.cloudstore.core.util.Util.*; 004 005import java.util.Arrays; 006import java.util.Comparator; 007import java.util.Date; 008import java.util.HashMap; 009import java.util.Map; 010import java.util.SortedSet; 011import java.util.Timer; 012import java.util.TimerTask; 013import java.util.TreeSet; 014import java.util.UUID; 015 016import co.codewizards.cloudstore.core.auth.AuthToken; 017import co.codewizards.cloudstore.core.config.Config; 018import co.codewizards.cloudstore.core.dto.DateTime; 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(UUID serverRepositoryId, UUID clientRepositoryId) { 062 assertNotNull("serverRepositoryId", serverRepositoryId); 063 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(UUID serverRepositoryId, UUID clientRepositoryId, char[] password) { 090 assertNotNull("serverRepositoryId", serverRepositoryId); 091 assertNotNull("clientRepositoryId", clientRepositoryId); 092 assertNotNull("password", password); 093 Map<UUID, SortedSet<TransientRepoPassword>> clientRepositoryId2AuthRepoPasswordSet = serverRepositoryId2ClientRepositoryId2AuthRepoPasswordSet.get(serverRepositoryId); 094 if (clientRepositoryId2AuthRepoPasswordSet == null) 095 return false; 096 097 SortedSet<TransientRepoPassword> authRepoPasswordSet = clientRepositoryId2AuthRepoPasswordSet.get(clientRepositoryId); 098 if (authRepoPasswordSet == null) 099 return false; 100 101 for (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 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 UUID serverRepositoryId = oldestAuthRepoPassword.getServerRepositoryId(); 119 UUID clientRepositoryId = oldestAuthRepoPassword.getClientRepositoryId(); 120 121 Map<UUID, SortedSet<TransientRepoPassword>> clientRepositoryId2AuthRepoPasswordSet = serverRepositoryId2ClientRepositoryId2AuthRepoPasswordSet.get(serverRepositoryId); 122 assertNotNull("clientRepositoryId2AuthRepoPasswordSet", clientRepositoryId2AuthRepoPasswordSet); 123 124 SortedSet<TransientRepoPassword> authRepoPasswordSet = clientRepositoryId2AuthRepoPasswordSet.get(clientRepositoryId); 125 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 = Config.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 = Config.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 = Config.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 = Config.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(TransientRepoPassword o1, TransientRepoPassword o2) { 172 Date expiryDate1 = o1.getAuthToken().getExpiryDateTime().toDate(); 173 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(TransientRepoPassword transientRepoPassword) { 191 assertNotNull("authRepoPassword", transientRepoPassword); 192 return System.currentTimeMillis() + getEarlyRenewalPeriod() > transientRepoPassword.getAuthToken().getRenewalDateTime().getMillis(); 193 } 194 195 private boolean isExpired(TransientRepoPassword transientRepoPassword) { 196 assertNotNull("authRepoPassword", transientRepoPassword); 197 return System.currentTimeMillis() > transientRepoPassword.getAuthToken().getExpiryDateTime().getMillis(); 198 } 199 200 private AuthToken createAuthToken() { 201 AuthToken authToken = new AuthToken(); 202 Date expiryDate = new Date(System.currentTimeMillis() + getValidityPeriod()); 203 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}