Username reservation table

This commit is contained in:
Moxie Marlinspike
2019-09-24 18:35:02 -07:00
parent 99c228dd6d
commit 523134f24b
6 changed files with 171 additions and 23 deletions

View File

@@ -157,13 +157,14 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
FaultTolerantDatabase messageDatabase = new FaultTolerantDatabase("message_database", messageJdbi, config.getMessageStoreConfiguration().getCircuitBreakerConfiguration());
FaultTolerantDatabase abuseDatabase = new FaultTolerantDatabase("abuse_database", abuseJdbi, config.getAbuseDatabaseConfiguration().getCircuitBreakerConfiguration());
Accounts accounts = new Accounts(accountDatabase);
PendingAccounts pendingAccounts = new PendingAccounts(accountDatabase);
PendingDevices pendingDevices = new PendingDevices(accountDatabase);
Usernames usernames = new Usernames(accountDatabase);
Keys keys = new Keys(keysDatabase);
Messages messages = new Messages(messageDatabase);
AbusiveHostRules abusiveHostRules = new AbusiveHostRules(abuseDatabase);
Accounts accounts = new Accounts(accountDatabase);
PendingAccounts pendingAccounts = new PendingAccounts(accountDatabase);
PendingDevices pendingDevices = new PendingDevices (accountDatabase);
Usernames usernames = new Usernames(accountDatabase);
ReservedUsernames reservedUsernames = new ReservedUsernames(accountDatabase);
Keys keys = new Keys(keysDatabase);
Messages messages = new Messages(messageDatabase);
AbusiveHostRules abusiveHostRules = new AbusiveHostRules(abuseDatabase);
RedisClientFactory cacheClientFactory = new RedisClientFactory("main_cache", config.getCacheConfiguration().getUrl(), config.getCacheConfiguration().getReplicaUrls(), config.getCacheConfiguration().getCircuitBreakerConfiguration());
RedisClientFactory directoryClientFactory = new RedisClientFactory("directory_cache", config.getDirectoryConfiguration().getRedisConfiguration().getUrl(), config.getDirectoryConfiguration().getRedisConfiguration().getReplicaUrls(), config.getDirectoryConfiguration().getRedisConfiguration().getCircuitBreakerConfiguration());
@@ -180,7 +181,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
PendingAccountsManager pendingAccountsManager = new PendingAccountsManager(pendingAccounts, cacheClient);
PendingDevicesManager pendingDevicesManager = new PendingDevicesManager (pendingDevices, cacheClient );
AccountsManager accountsManager = new AccountsManager(accounts, directory, cacheClient);
UsernamesManager usernamesManager = new UsernamesManager(usernames, cacheClient);
UsernamesManager usernamesManager = new UsernamesManager(usernames, reservedUsernames, cacheClient);
MessagesCache messagesCache = new MessagesCache(messagesClient, messages, accountsManager, config.getMessageCacheConfiguration().getPersistDelayMinutes());
MessagesManager messagesManager = new MessagesManager(messages, messagesCache);
DeadLetterHandler deadLetterHandler = new DeadLetterHandler(messagesManager);

View File

@@ -0,0 +1,53 @@
package org.whispersystems.textsecuregcm.storage;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.Timer;
import com.google.common.annotations.VisibleForTesting;
import org.whispersystems.textsecuregcm.util.Constants;
import java.util.Optional;
import java.util.UUID;
import static com.codahale.metrics.MetricRegistry.name;
public class ReservedUsernames {
public static final String ID = "id";
public static final String UID = "uuid";
public static final String USERNAME = "username";
private final MetricRegistry metricRegistry = SharedMetricRegistries.getOrCreate(Constants.METRICS_NAME);
private final Timer queryTimer = metricRegistry.timer(name(ReservedUsernames.class, "query"));
private final FaultTolerantDatabase database;
public ReservedUsernames(FaultTolerantDatabase database) {
this.database = database;
}
public boolean isReserved(String username, UUID uuid) {
return database.with(jdbi -> jdbi.withHandle(handle -> {
try (Timer.Context ignored = queryTimer.time()) {
Optional<Integer> reservations = handle.createQuery("SELECT COUNT(*) FROM reserved_usernames WHERE " + UID + " != :uuid AND :username ~* " + USERNAME)
.bind("username", username)
.bind("uuid", uuid)
.mapTo(Integer.class)
.findFirst();
return reservations.isPresent() && reservations.get() > 0;
}
}));
}
@VisibleForTesting
public void setReserved(String username, UUID reservedFor) {
database.use(jdbi -> jdbi.useHandle(handle -> {
handle.createUpdate("INSERT INTO reserved_usernames (" + USERNAME + ", " + UID + ") VALUES(:username, :uuid)")
.bind("username", username)
.bind("uuid", reservedFor)
.execute();
}));
}
}

View File

@@ -30,15 +30,21 @@ public class UsernamesManager {
private final Logger logger = LoggerFactory.getLogger(AccountsManager.class);
private final Usernames usernames;
private final ReservedUsernames reservedUsernames;
private final ReplicatedJedisPool cacheClient;
public UsernamesManager(Usernames usernames, ReplicatedJedisPool cacheClient) {
this.usernames = usernames;
this.cacheClient = cacheClient;
public UsernamesManager(Usernames usernames, ReservedUsernames reservedUsernames, ReplicatedJedisPool cacheClient) {
this.usernames = usernames;
this.reservedUsernames = reservedUsernames;
this.cacheClient = cacheClient;
}
public boolean put(UUID uuid, String username) {
try (Timer.Context ignored = createTimer.time()) {
if (reservedUsernames.isReserved(username, uuid)) {
return false;
}
if (databasePut(uuid, username)) {
redisSet(uuid, username);