#437 Add/change email should check if email is already used

- Untested/incomplete implementation
This commit is contained in:
ljacqu 2016-01-17 20:41:19 +01:00
parent ace95f750a
commit b0ba893827
11 changed files with 105 additions and 177 deletions

View File

@ -289,4 +289,9 @@ public class CacheDataSource implements DataSource {
public List<PlayerAuth> getLoggedPlayers() { public List<PlayerAuth> getLoggedPlayers() {
return new ArrayList<>(PlayerCache.getInstance().getCache().values()); return new ArrayList<>(PlayerCache.getInstance().getCache().values());
} }
@Override
public boolean isEmailStored(String email) {
return source.isEmailStored(email);
}
} }

View File

@ -218,6 +218,8 @@ public interface DataSource {
*/ */
List<PlayerAuth> getLoggedPlayers(); List<PlayerAuth> getLoggedPlayers();
boolean isEmailStored(String email);
enum DataSourceType { enum DataSourceType {
MYSQL, MYSQL,
FILE, FILE,

View File

@ -52,13 +52,6 @@ public class FlatFile implements DataSource {
} }
} }
/**
* Method isAuthAvailable.
*
* @param user String
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#isAuthAvailable(String)
*/
@Override @Override
public synchronized boolean isAuthAvailable(String user) { public synchronized boolean isAuthAvailable(String user) {
BufferedReader br = null; BufferedReader br = null;
@ -97,13 +90,6 @@ public class FlatFile implements DataSource {
return null; return null;
} }
/**
* Method saveAuth.
*
* @param auth PlayerAuth
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#saveAuth(PlayerAuth)
*/
@Override @Override
public synchronized boolean saveAuth(PlayerAuth auth) { public synchronized boolean saveAuth(PlayerAuth auth) {
if (isAuthAvailable(auth.getNickname())) { if (isAuthAvailable(auth.getNickname())) {
@ -127,13 +113,6 @@ public class FlatFile implements DataSource {
return true; return true;
} }
/**
* Method updatePassword.
*
* @param auth PlayerAuth
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#updatePassword(PlayerAuth)
*/
@Override @Override
public synchronized boolean updatePassword(PlayerAuth auth) { public synchronized boolean updatePassword(PlayerAuth auth) {
return updatePassword(auth.getNickname(), auth.getPassword()); return updatePassword(auth.getNickname(), auth.getPassword());
@ -200,13 +179,6 @@ public class FlatFile implements DataSource {
return true; return true;
} }
/**
* Method updateSession.
*
* @param auth PlayerAuth
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#updateSession(PlayerAuth)
*/
@Override @Override
public boolean updateSession(PlayerAuth auth) { public boolean updateSession(PlayerAuth auth) {
if (!isAuthAvailable(auth.getNickname())) { if (!isAuthAvailable(auth.getNickname())) {
@ -266,13 +238,6 @@ public class FlatFile implements DataSource {
return true; return true;
} }
/**
* Method updateQuitLoc.
*
* @param auth PlayerAuth
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#updateQuitLoc(PlayerAuth)
*/
@Override @Override
public boolean updateQuitLoc(PlayerAuth auth) { public boolean updateQuitLoc(PlayerAuth auth) {
if (!isAuthAvailable(auth.getNickname())) { if (!isAuthAvailable(auth.getNickname())) {
@ -311,13 +276,6 @@ public class FlatFile implements DataSource {
return true; return true;
} }
/**
* Method getIps.
*
* @param ip String
*
* @return int * @see fr.xephi.authme.datasource.DataSource#getIps(String)
*/
@Override @Override
public int getIps(String ip) { public int getIps(String ip) {
BufferedReader br = null; BufferedReader br = null;
@ -348,13 +306,6 @@ public class FlatFile implements DataSource {
} }
} }
/**
* Method purgeDatabase.
*
* @param until long
*
* @return int * @see fr.xephi.authme.datasource.DataSource#purgeDatabase(long)
*/
@Override @Override
public int purgeDatabase(long until) { public int purgeDatabase(long until) {
BufferedReader br = null; BufferedReader br = null;
@ -401,13 +352,6 @@ public class FlatFile implements DataSource {
return cleared; return cleared;
} }
/**
* Method autoPurgeDatabase.
*
* @param until long
*
* @return List of String * @see fr.xephi.authme.datasource.DataSource#autoPurgeDatabase(long)
*/
@Override @Override
public List<String> autoPurgeDatabase(long until) { public List<String> autoPurgeDatabase(long until) {
BufferedReader br = null; BufferedReader br = null;
@ -454,13 +398,6 @@ public class FlatFile implements DataSource {
return cleared; return cleared;
} }
/**
* Method removeAuth.
*
* @param user String
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#removeAuth(String)
*/
@Override @Override
public synchronized boolean removeAuth(String user) { public synchronized boolean removeAuth(String user) {
if (!isAuthAvailable(user)) { if (!isAuthAvailable(user)) {
@ -505,13 +442,6 @@ public class FlatFile implements DataSource {
return true; return true;
} }
/**
* Method getAuth.
*
* @param user String
*
* @return PlayerAuth * @see fr.xephi.authme.datasource.DataSource#getAuth(String)
*/
@Override @Override
public synchronized PlayerAuth getAuth(String user) { public synchronized PlayerAuth getAuth(String user) {
BufferedReader br = null; BufferedReader br = null;
@ -554,31 +484,14 @@ public class FlatFile implements DataSource {
return null; return null;
} }
/**
* Method close.
*
* @see fr.xephi.authme.datasource.DataSource#close()
*/
@Override @Override
public synchronized void close() { public synchronized void close() {
} }
/**
* Method reload.
*
* @see fr.xephi.authme.datasource.DataSource#reload()
*/
@Override @Override
public void reload() { public void reload() {
} }
/**
* Method updateEmail.
*
* @param auth PlayerAuth
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#updateEmail(PlayerAuth)
*/
@Override @Override
public boolean updateEmail(PlayerAuth auth) { public boolean updateEmail(PlayerAuth auth) {
if (!isAuthAvailable(auth.getNickname())) { if (!isAuthAvailable(auth.getNickname())) {
@ -617,13 +530,6 @@ public class FlatFile implements DataSource {
return true; return true;
} }
/**
* Method getAllAuthsByName.
*
* @param auth PlayerAuth
*
* @return List of String * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByName(PlayerAuth)
*/
@Override @Override
public List<String> getAllAuthsByName(PlayerAuth auth) { public List<String> getAllAuthsByName(PlayerAuth auth) {
BufferedReader br = null; BufferedReader br = null;
@ -654,13 +560,6 @@ public class FlatFile implements DataSource {
} }
} }
/**
* Method getAllAuthsByIp.
*
* @param ip String
*
* @return List of String * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByIp(String)
*/
@Override @Override
public List<String> getAllAuthsByIp(String ip) { public List<String> getAllAuthsByIp(String ip) {
BufferedReader br = null; BufferedReader br = null;
@ -691,13 +590,6 @@ public class FlatFile implements DataSource {
} }
} }
/**
* Method getAllAuthsByEmail.
*
* @param email String
*
* @return List of String * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByEmail(String)
*/
@Override @Override
public List<String> getAllAuthsByEmail(String email) { public List<String> getAllAuthsByEmail(String email) {
BufferedReader br = null; BufferedReader br = null;
@ -728,13 +620,6 @@ public class FlatFile implements DataSource {
} }
} }
/**
* Method purgeBanned.
*
* @param banned List of String
*
* @see fr.xephi.authme.datasource.DataSource#purgeBanned(List)
*/
@Override @Override
public void purgeBanned(List<String> banned) { public void purgeBanned(List<String> banned) {
BufferedReader br = null; BufferedReader br = null;
@ -776,64 +661,28 @@ public class FlatFile implements DataSource {
} }
} }
/**
* Method getType.
*
* @return DataSourceType * @see fr.xephi.authme.datasource.DataSource#getType()
*/
@Override @Override
public DataSourceType getType() { public DataSourceType getType() {
return DataSourceType.FILE; return DataSourceType.FILE;
} }
/**
* Method isLogged.
*
* @param user String
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#isLogged(String)
*/
@Override @Override
public boolean isLogged(String user) { public boolean isLogged(String user) {
return PlayerCache.getInstance().isAuthenticated(user); return PlayerCache.getInstance().isAuthenticated(user);
} }
/**
* Method setLogged.
*
* @param user String
*
* @see fr.xephi.authme.datasource.DataSource#setLogged(String)
*/
@Override @Override
public void setLogged(String user) { public void setLogged(String user) {
} }
/**
* Method setUnlogged.
*
* @param user String
*
* @see fr.xephi.authme.datasource.DataSource#setUnlogged(String)
*/
@Override @Override
public void setUnlogged(String user) { public void setUnlogged(String user) {
} }
/**
* Method purgeLogged.
*
* @see fr.xephi.authme.datasource.DataSource#purgeLogged()
*/
@Override @Override
public void purgeLogged() { public void purgeLogged() {
} }
/**
* Method getAccountsRegistered.
*
* @return int * @see fr.xephi.authme.datasource.DataSource#getAccountsRegistered()
*/
@Override @Override
public int getAccountsRegistered() { public int getAccountsRegistered() {
BufferedReader br = null; BufferedReader br = null;
@ -857,14 +706,6 @@ public class FlatFile implements DataSource {
return result; return result;
} }
/**
* Method updateName.
*
* @param oldOne String
* @param newOne String
*
* @see fr.xephi.authme.datasource.DataSource#updateName(String, String)
*/
@Override @Override
public void updateName(String oldOne, String newOne) { public void updateName(String oldOne, String newOne) {
PlayerAuth auth = this.getAuth(oldOne); PlayerAuth auth = this.getAuth(oldOne);
@ -873,11 +714,6 @@ public class FlatFile implements DataSource {
this.removeAuth(oldOne); this.removeAuth(oldOne);
} }
/**
* Method getAllAuths.
*
* @return List of PlayerAuth * @see fr.xephi.authme.datasource.DataSource#getAllAuths()
*/
@Override @Override
public List<PlayerAuth> getAllAuths() { public List<PlayerAuth> getAllAuths() {
BufferedReader br = null; BufferedReader br = null;
@ -925,13 +761,13 @@ public class FlatFile implements DataSource {
return auths; return auths;
} }
/**
* Method getLoggedPlayers.
*
* @return List of PlayerAuth * @see fr.xephi.authme.datasource.DataSource#getLoggedPlayers()
*/
@Override @Override
public List<PlayerAuth> getLoggedPlayers() { public List<PlayerAuth> getLoggedPlayers() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override
public boolean isEmailStored(String email) {
throw new UnsupportedOperationException("Flat file no longer supported");
}
} }

View File

@ -971,9 +971,8 @@ public class MySQL implements DataSource {
pst.close(); pst.close();
rs.close(); rs.close();
st.close(); st.close();
} catch (Exception ex) { } catch (SQLException ex) {
ConsoleLogger.showError(ex.getMessage()); logSqlException(ex);
ConsoleLogger.writeStackTrace(ex);
} }
return auths; return auths;
} }
@ -1015,11 +1014,29 @@ public class MySQL implements DataSource {
} }
auths.add(pAuth); auths.add(pAuth);
} }
} catch (Exception ex) { } catch (SQLException ex) {
ConsoleLogger.showError(ex.getMessage()); logSqlException(ex);
ConsoleLogger.writeStackTrace(ex);
} }
return auths; return auths;
} }
@Override
public synchronized boolean isEmailStored(String email) {
String sql = "SELECT 1 FROM " + tableName + " WHERE " + columnEmail + " = ?";
try (Connection con = ds.getConnection()) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, email);
ResultSet rs = pst.executeQuery();
return rs.next();
} catch (SQLException e) {
logSqlException(e);
}
return false;
}
private static void logSqlException(SQLException e) {
ConsoleLogger.showError("Error executing SQL query: " + StringUtils.formatException(e));
ConsoleLogger.writeStackTrace(e);
}
} }

View File

@ -684,6 +684,25 @@ public class SQLite implements DataSource {
return auths; return auths;
} }
@Override
public synchronized boolean isEmailStored(String email) {
try {
PreparedStatement ps = con.prepareStatement(
"SELECT 1 FROM " + tableName + " WHERE LOWER(" + columnEmail + ") = LOWER(?)");
ps.setString(1, email);
ResultSet rs = ps.executeQuery();
return rs.next();
} catch (SQLException e) {
logSqlException(e);
}
return false;
}
private static void logSqlException(SQLException e) {
ConsoleLogger.showError("Error while executing SQL statement: " + StringUtils.formatException(e));
ConsoleLogger.writeStackTrace(e);
}
private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException { private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException {
String salt = !columnSalt.isEmpty() ? row.getString(columnSalt) : null; String salt = !columnSalt.isEmpty() ? row.getString(columnSalt) : null;

View File

@ -121,7 +121,9 @@ public enum MessageKey {
ANTIBOT_AUTO_ENABLED_MESSAGE("antibot_auto_enabled"), ANTIBOT_AUTO_ENABLED_MESSAGE("antibot_auto_enabled"),
ANTIBOT_AUTO_DISABLED_MESSAGE("antibot_auto_disabled", "%m"); ANTIBOT_AUTO_DISABLED_MESSAGE("antibot_auto_disabled", "%m"),
EMAIL_ALREADY_USED_ERROR("email_already_used");
private String key; private String key;

View File

@ -36,10 +36,13 @@ public class AsyncAddEmail {
PlayerAuth auth = playerCache.getAuth(playerName); PlayerAuth auth = playerCache.getAuth(playerName);
String currentEmail = auth.getEmail(); String currentEmail = auth.getEmail();
if (currentEmail != null) { if (currentEmail != null && !"your@mail.com".equals(currentEmail)) {
System.out.println("Email is currentEmail " + currentEmail); // FIXME remove
messages.send(player, MessageKey.USAGE_CHANGE_EMAIL); messages.send(player, MessageKey.USAGE_CHANGE_EMAIL);
} else if (isEmailInvalid(email)) { } else if (isEmailInvalid(email)) {
messages.send(player, MessageKey.INVALID_EMAIL); messages.send(player, MessageKey.INVALID_EMAIL);
} else if (dataSource.isEmailStored(email)) {
messages.send(player, MessageKey.EMAIL_ALREADY_USED_ERROR);
} else { } else {
auth.setEmail(email); auth.setEmail(email);
playerCache.updatePlayer(auth); playerCache.updatePlayer(auth);

View File

@ -44,6 +44,8 @@ public class AsyncChangeEmail {
m.send(player, MessageKey.INVALID_NEW_EMAIL); m.send(player, MessageKey.INVALID_NEW_EMAIL);
} else if (!oldEmail.equals(currentEmail)) { } else if (!oldEmail.equals(currentEmail)) {
m.send(player, MessageKey.INVALID_OLD_EMAIL); m.send(player, MessageKey.INVALID_OLD_EMAIL);
} else if (dataSource.isEmailStored(newEmail)) {
m.send(player, MessageKey.EMAIL_ALREADY_USED_ERROR);
} else { } else {
saveNewEmail(auth); saveNewEmail(auth);
} }

View File

@ -57,3 +57,4 @@ email_exists: '&cA recovery email was already sent! You can discard it and send
country_banned: '&4Your country is banned from this server!' country_banned: '&4Your country is banned from this server!'
antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!'
antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!' antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!'
email_already_used: '&4The email address is already being used'

View File

@ -53,6 +53,7 @@ public class AsyncAddEmailTest {
PlayerAuth auth = mock(PlayerAuth.class); PlayerAuth auth = mock(PlayerAuth.class);
given(auth.getEmail()).willReturn(null); given(auth.getEmail()).willReturn(null);
given(playerCache.getAuth("tester")).willReturn(auth); given(playerCache.getAuth("tester")).willReturn(auth);
given(dataSource.isEmailStored("my.mail@example.org")).willReturn(false);
// when // when
process.process(); process.process();
@ -72,6 +73,7 @@ public class AsyncAddEmailTest {
PlayerAuth auth = mock(PlayerAuth.class); PlayerAuth auth = mock(PlayerAuth.class);
given(auth.getEmail()).willReturn("another@mail.tld"); given(auth.getEmail()).willReturn("another@mail.tld");
given(playerCache.getAuth("my_player")).willReturn(auth); given(playerCache.getAuth("my_player")).willReturn(auth);
given(dataSource.isEmailStored("some.mail@example.org")).willReturn(false);
// when // when
process.process(); process.process();
@ -90,6 +92,7 @@ public class AsyncAddEmailTest {
PlayerAuth auth = mock(PlayerAuth.class); PlayerAuth auth = mock(PlayerAuth.class);
given(auth.getEmail()).willReturn(null); given(auth.getEmail()).willReturn(null);
given(playerCache.getAuth("my_player")).willReturn(auth); given(playerCache.getAuth("my_player")).willReturn(auth);
given(dataSource.isEmailStored("invalid_mail")).willReturn(false);
// when // when
process.process(); process.process();
@ -99,6 +102,25 @@ public class AsyncAddEmailTest {
verify(playerCache, never()).updatePlayer(any(PlayerAuth.class)); verify(playerCache, never()).updatePlayer(any(PlayerAuth.class));
} }
@Test
public void shouldNotAddMailIfAlreadyUsed() {
// given
AsyncAddEmail process = createProcess("player@mail.tld");
given(player.getName()).willReturn("TestName");
given(playerCache.isAuthenticated("testname")).willReturn(true);
PlayerAuth auth = mock(PlayerAuth.class);
given(auth.getEmail()).willReturn(null);
given(playerCache.getAuth("testname")).willReturn(auth);
given(dataSource.isEmailStored("player@mail.tld")).willReturn(true);
// when
process.process();
// then
verify(messages).send(player, MessageKey.EMAIL_ALREADY_USED_ERROR);
verify(playerCache, never()).updatePlayer(any(PlayerAuth.class));
}
@Test @Test
public void shouldShowLoginMessage() { public void shouldShowLoginMessage() {
// given // given

View File

@ -136,6 +136,25 @@ public class AsyncChangeEmailTest {
verify(messages).send(player, MessageKey.INVALID_OLD_EMAIL); verify(messages).send(player, MessageKey.INVALID_OLD_EMAIL);
} }
@Test
public void shouldRejectAlreadyUsedEmail() {
// given
AsyncChangeEmail process = createProcess("old@example.com", "new@example.com");
given(player.getName()).willReturn("Username");
given(playerCache.isAuthenticated("username")).willReturn(true);
PlayerAuth auth = authWithMail("old@example.com");
given(playerCache.getAuth("username")).willReturn(auth);
given(dataSource.isEmailStored("new@example.com")).willReturn(true);
// when
process.process();
// then
verify(dataSource, never()).updateEmail(any(PlayerAuth.class));
verify(playerCache, never()).updatePlayer(any(PlayerAuth.class));
verify(messages).send(player, MessageKey.EMAIL_ALREADY_USED_ERROR);
}
@Test @Test
public void shouldSendLoginMessage() { public void shouldSendLoginMessage() {
// given // given