diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java index efe9251ea..a7f327a10 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/GetEmailCommand.java @@ -1,8 +1,8 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.command.ExecutableCommand; -import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.datasource.DataSourceResult; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; @@ -25,11 +25,11 @@ public class GetEmailCommand implements ExecutableCommand { public void executeCommand(CommandSender sender, List arguments) { String playerName = arguments.isEmpty() ? sender.getName() : arguments.get(0); - PlayerAuth auth = dataSource.getAuth(playerName); - if (auth == null) { - commonService.send(sender, MessageKey.UNKNOWN_USER); + DataSourceResult email = dataSource.getEmail(playerName); + if (email.playerExists()) { + sender.sendMessage("[AuthMe] " + playerName + "'s email: " + email.getValue()); } else { - sender.sendMessage("[AuthMe] " + playerName + "'s email: " + auth.getEmail()); + commonService.send(sender, MessageKey.UNKNOWN_USER); } } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java b/src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java index 04ba276a6..38dbf14aa 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/debug/TestEmailSender.java @@ -1,8 +1,8 @@ package fr.xephi.authme.command.executable.authme.debug; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.datasource.DataSourceResult; import fr.xephi.authme.mail.SendMailSsl; import fr.xephi.authme.util.StringUtils; import org.apache.commons.mail.EmailException; @@ -62,13 +62,13 @@ class TestEmailSender implements DebugSection { private String getEmail(CommandSender sender, List arguments) { if (arguments.isEmpty()) { - PlayerAuth auth = dataSource.getAuth(sender.getName()); - if (auth == null) { + DataSourceResult emailResult = dataSource.getEmail(sender.getName()); + if (!emailResult.playerExists()) { sender.sendMessage(ChatColor.RED + "Please provide an email address, " + "e.g. /authme debug mail test@example.com"); return null; } - String email = auth.getEmail(); + final String email = emailResult.getValue(); if (email == null || "your@email.com".equals(email)) { sender.sendMessage(ChatColor.RED + "No email set for your account!" + " Please use /authme debug mail "); diff --git a/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java index bd50cc743..1e5ece77f 100644 --- a/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/email/RecoverEmailCommand.java @@ -2,9 +2,9 @@ package fr.xephi.authme.command.executable.email; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.PlayerCommand; -import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.datasource.DataSourceResult; import fr.xephi.authme.mail.EmailService; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.CommonService; @@ -53,13 +53,13 @@ public class RecoverEmailCommand extends PlayerCommand { return; } - PlayerAuth auth = dataSource.getAuth(playerName); // TODO #1127: Create method to get email only - if (auth == null) { + DataSourceResult emailResult = dataSource.getEmail(playerName); + if (!emailResult.playerExists()) { commonService.send(player, MessageKey.USAGE_REGISTER); return; } - final String email = auth.getEmail(); + final String email = emailResult.getValue(); if (email == null || !email.equalsIgnoreCase(playerMail) || "your@email.com".equalsIgnoreCase(email)) { commonService.send(player, MessageKey.INVALID_EMAIL); return; diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java index fbbaa5076..2e304a991 100644 --- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java @@ -23,16 +23,20 @@ import java.util.concurrent.TimeUnit; public class CacheDataSource implements DataSource { private final DataSource source; + private final PlayerCache playerCache; private final LoadingCache> cachedAuths; private final ListeningExecutorService executorService; /** * Constructor for CacheDataSource. * - * @param src DataSource + * @param source the source + * @param playerCache the player cache */ - public CacheDataSource(DataSource src) { - source = src; + public CacheDataSource(DataSource source, PlayerCache playerCache) { + this.source = source; + this.playerCache = playerCache; + executorService = MoreExecutors.listeningDecorator( Executors.newCachedThreadPool(new ThreadFactoryBuilder() .setDaemon(true) @@ -168,17 +172,17 @@ public class CacheDataSource implements DataSource { } @Override - public List getAllAuthsByIp(final String ip) { + public List getAllAuthsByIp(String ip) { return source.getAllAuthsByIp(ip); } @Override - public int countAuthsByEmail(final String email) { + public int countAuthsByEmail(String email) { return source.countAuthsByEmail(email); } @Override - public void purgeRecords(final Collection banned) { + public void purgeRecords(Collection banned) { source.purgeRecords(banned); cachedAuths.invalidateAll(banned); } @@ -190,7 +194,7 @@ public class CacheDataSource implements DataSource { @Override public boolean isLogged(String user) { - return PlayerCache.getInstance().isAuthenticated(user); + return playerCache.isAuthenticated(user); } @Override @@ -223,6 +227,13 @@ public class CacheDataSource implements DataSource { return result; } + @Override + public DataSourceResult getEmail(String user) { + return cachedAuths.getUnchecked(user) + .map(auth -> DataSourceResult.of(auth.getEmail())) + .orElse(DataSourceResult.unknownPlayer()); + } + @Override public List getAllAuths() { return source.getAllAuths(); @@ -230,6 +241,6 @@ public class CacheDataSource implements DataSource { @Override public List getLoggedPlayers() { - return new ArrayList<>(PlayerCache.getInstance().getCache().values()); + return new ArrayList<>(playerCache.getCache().values()); } } diff --git a/src/main/java/fr/xephi/authme/datasource/DataSource.java b/src/main/java/fr/xephi/authme/datasource/DataSource.java index a1f50163a..080998aeb 100644 --- a/src/main/java/fr/xephi/authme/datasource/DataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/DataSource.java @@ -189,6 +189,14 @@ public interface DataSource extends Reloadable { */ boolean updateRealName(String user, String realName); + /** + * Returns the email of the user. + * + * @param user the user to retrieve an email for + * @return the email saved for the user, or null if user or email is not present + */ + DataSourceResult getEmail(String user); + /** * Return all players of the database. * diff --git a/src/main/java/fr/xephi/authme/datasource/DataSourceResult.java b/src/main/java/fr/xephi/authme/datasource/DataSourceResult.java new file mode 100644 index 000000000..f556f853b --- /dev/null +++ b/src/main/java/fr/xephi/authme/datasource/DataSourceResult.java @@ -0,0 +1,53 @@ +package fr.xephi.authme.datasource; + +/** + * Wraps a value and allows to specify whether a value is missing or the player is not registered. + */ +public class DataSourceResult { + + /** Instance used when a player does not exist. */ + private static final DataSourceResult UNKNOWN_PLAYER = new DataSourceResult<>(null); + private final T value; + + private DataSourceResult(T value) { + this.value = value; + } + + /** + * Returns a {@link DataSourceResult} for the given value. + * + * @param value the value to wrap + * @param the value's type + * @return DataSourceResult object for the given value + */ + public static DataSourceResult of(T value) { + return new DataSourceResult<>(value); + } + + /** + * Returns a {@link DataSourceResult} specifying that the player does not exist. + * + * @param the value type + * @return data source result for unknown player + */ + public static DataSourceResult unknownPlayer() { + return UNKNOWN_PLAYER; + } + + /** + * @return whether the player of the associated value exists + */ + public boolean playerExists() { + return this != UNKNOWN_PLAYER; + } + + /** + * Returns the value. It is {@code null} if the player is unknown. It is also {@code null} + * if the player exists but does not have the value defined. + * + * @return the value, or null + */ + public T getValue() { + return value; + } +} diff --git a/src/main/java/fr/xephi/authme/datasource/FlatFile.java b/src/main/java/fr/xephi/authme/datasource/FlatFile.java index 8c1ecf4b5..95e2689a4 100644 --- a/src/main/java/fr/xephi/authme/datasource/FlatFile.java +++ b/src/main/java/fr/xephi/authme/datasource/FlatFile.java @@ -396,6 +396,11 @@ public class FlatFile implements DataSource { throw new UnsupportedOperationException("Flat file no longer supported"); } + @Override + public DataSourceResult getEmail(String user) { + throw new UnsupportedOperationException("Flat file no longer supported"); + } + @Override public List getAllAuths() { BufferedReader br = null; diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index 6e09b5853..148ea26ea 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -879,6 +879,22 @@ public class MySQL implements DataSource { return false; } + @Override + public DataSourceResult getEmail(String user) { + String sql = "SELECT " + col.EMAIL + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; + try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { + pst.setString(1, user); + try (ResultSet rs = pst.executeQuery()) { + if (rs.next()) { + return DataSourceResult.of(rs.getString(1)); + } + } + } catch (SQLException ex) { + logSqlException(ex); + } + return DataSourceResult.unknownPlayer(); + } + @Override public List getAllAuths() { List auths = new ArrayList<>(); diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java index fdb9c9197..6212506ba 100644 --- a/src/main/java/fr/xephi/authme/datasource/SQLite.java +++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java @@ -559,6 +559,22 @@ public class SQLite implements DataSource { return false; } + @Override + public DataSourceResult getEmail(String user) { + String sql = "SELECT " + col.EMAIL + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; + try (PreparedStatement pst = con.prepareStatement(sql)) { + pst.setString(1, user); + try (ResultSet rs = pst.executeQuery()) { + if (rs.next()) { + return DataSourceResult.of(rs.getString(1)); + } + } + } catch (SQLException ex) { + logSqlException(ex); + } + return DataSourceResult.unknownPlayer(); + } + @Override public List getAllAuths() { List auths = new ArrayList<>(); diff --git a/src/main/java/fr/xephi/authme/initialization/DataSourceProvider.java b/src/main/java/fr/xephi/authme/initialization/DataSourceProvider.java index 686059539..594cabf4e 100644 --- a/src/main/java/fr/xephi/authme/initialization/DataSourceProvider.java +++ b/src/main/java/fr/xephi/authme/initialization/DataSourceProvider.java @@ -1,6 +1,7 @@ package fr.xephi.authme.initialization; import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.CacheDataSource; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSourceType; @@ -33,6 +34,8 @@ public class DataSourceProvider implements Provider { private Settings settings; @Inject private BukkitService bukkitService; + @Inject + private PlayerCache playerCache; DataSourceProvider() { } @@ -76,7 +79,7 @@ public class DataSourceProvider implements Provider { dataSource = convertFlatfileToSqlite(dataSource); if (settings.getProperty(DatabaseSettings.USE_CACHING)) { - dataSource = new CacheDataSource(dataSource); + dataSource = new CacheDataSource(dataSource, playerCache); } if (DataSourceType.SQLITE.equals(dataSourceType)) { checkDataSourceSize(dataSource, bukkitService); diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java index 1e3fe3915..07c92d13c 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/GetEmailCommandTest.java @@ -1,7 +1,7 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.datasource.DataSourceResult; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.service.CommonService; import org.bukkit.command.CommandSender; @@ -38,7 +38,7 @@ public class GetEmailCommandTest { public void shouldReportUnknownUser() { // given String user = "myTestUser"; - given(dataSource.getAuth(user)).willReturn(null); + given(dataSource.getEmail(user)).willReturn(DataSourceResult.unknownPlayer()); CommandSender sender = mock(CommandSender.class); // when @@ -53,9 +53,7 @@ public class GetEmailCommandTest { // given String user = "userToView"; String email = "user.email@example.org"; - PlayerAuth auth = mock(PlayerAuth.class); - given(auth.getEmail()).willReturn(email); - given(dataSource.getAuth(user)).willReturn(auth); + given(dataSource.getEmail(user)).willReturn(DataSourceResult.of(email)); CommandSender sender = mock(CommandSender.class); // when diff --git a/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java index 8b026fc52..b6c08eaa7 100644 --- a/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/email/RecoverEmailCommandTest.java @@ -4,9 +4,9 @@ import ch.jalu.injector.testing.BeforeInjecting; import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.InjectDelayed; import fr.xephi.authme.TestHelper; -import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.datasource.DataSourceResult; import fr.xephi.authme.mail.EmailService; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.security.PasswordSecurity; @@ -111,14 +111,14 @@ public class RecoverEmailCommandTest { given(sender.getName()).willReturn(name); given(emailService.hasAllInformation()).willReturn(true); given(playerCache.isAuthenticated(name)).willReturn(false); - given(dataSource.getAuth(name)).willReturn(null); + given(dataSource.getEmail(name)).willReturn(DataSourceResult.unknownPlayer()); // when command.executeCommand(sender, Collections.singletonList("someone@example.com")); // then verify(emailService).hasAllInformation(); - verify(dataSource).getAuth(name); + verify(dataSource).getEmail(name); verifyNoMoreInteractions(dataSource); verify(commonService).send(sender, MessageKey.USAGE_REGISTER); } @@ -131,14 +131,14 @@ public class RecoverEmailCommandTest { given(sender.getName()).willReturn(name); given(emailService.hasAllInformation()).willReturn(true); given(playerCache.isAuthenticated(name)).willReturn(false); - given(dataSource.getAuth(name)).willReturn(newAuthWithEmail(DEFAULT_EMAIL)); + given(dataSource.getEmail(name)).willReturn(DataSourceResult.of(DEFAULT_EMAIL)); // when command.executeCommand(sender, Collections.singletonList(DEFAULT_EMAIL)); // then verify(emailService).hasAllInformation(); - verify(dataSource).getAuth(name); + verify(dataSource).getEmail(name); verifyNoMoreInteractions(dataSource); verify(commonService).send(sender, MessageKey.INVALID_EMAIL); } @@ -151,14 +151,14 @@ public class RecoverEmailCommandTest { given(sender.getName()).willReturn(name); given(emailService.hasAllInformation()).willReturn(true); given(playerCache.isAuthenticated(name)).willReturn(false); - given(dataSource.getAuth(name)).willReturn(newAuthWithEmail("raptor@example.org")); + given(dataSource.getEmail(name)).willReturn(DataSourceResult.of("raptor@example.org")); // when command.executeCommand(sender, Collections.singletonList("wrong-email@example.com")); // then verify(emailService).hasAllInformation(); - verify(dataSource).getAuth(name); + verify(dataSource).getEmail(name); verifyNoMoreInteractions(dataSource); verify(commonService).send(sender, MessageKey.INVALID_EMAIL); } @@ -173,7 +173,7 @@ public class RecoverEmailCommandTest { given(emailService.sendRecoveryCode(anyString(), anyString(), anyString())).willReturn(true); given(playerCache.isAuthenticated(name)).willReturn(false); String email = "v@example.com"; - given(dataSource.getAuth(name)).willReturn(newAuthWithEmail(email)); + given(dataSource.getEmail(name)).willReturn(DataSourceResult.of(email)); String code = "a94f37"; given(recoveryCodeService.isRecoveryCodeNeeded()).willReturn(true); given(recoveryCodeService.generateCode(name)).willReturn(code); @@ -183,7 +183,7 @@ public class RecoverEmailCommandTest { // then verify(emailService).hasAllInformation(); - verify(dataSource).getAuth(name); + verify(dataSource).getEmail(name); verify(recoveryService).createAndSendRecoveryCode(sender, email); } @@ -197,8 +197,7 @@ public class RecoverEmailCommandTest { given(emailService.sendPasswordMail(anyString(), anyString(), anyString())).willReturn(true); given(playerCache.isAuthenticated(name)).willReturn(false); String email = "vulture@example.com"; - PlayerAuth auth = newAuthWithEmail(email); - given(dataSource.getAuth(name)).willReturn(auth); + given(dataSource.getEmail(name)).willReturn(DataSourceResult.of(email)); given(recoveryCodeService.isRecoveryCodeNeeded()).willReturn(false); // when @@ -206,14 +205,7 @@ public class RecoverEmailCommandTest { // then verify(emailService).hasAllInformation(); - verify(dataSource).getAuth(name); + verify(dataSource).getEmail(name); verify(recoveryService).generateAndSendNewPassword(sender, email); } - - private static PlayerAuth newAuthWithEmail(String email) { - return PlayerAuth.builder() - .name("name") - .email(email) - .build(); - } } diff --git a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java index 3c8523bb3..ddf83bfae 100644 --- a/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/datasource/AbstractDataSourceIntegrationTest.java @@ -18,6 +18,7 @@ import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeThat; @@ -383,4 +384,20 @@ public abstract class AbstractDataSourceIntegrationTest { // then assertThat(dataSource.getAllAuths(), empty()); } + + @Test + public void shouldFetchEmail() { + // given + String user1 = "user"; + String user2 = "Bogus"; + DataSource dataSource = getDataSource(); + + // when + DataSourceResult email1 = dataSource.getEmail(user1); + DataSourceResult email2 = dataSource.getEmail(user2); + + // then + assertThat(email1.getValue(), equalTo("user@example.org")); + assertThat(email2, is(DataSourceResult.unknownPlayer())); + } }