#472 Create recovery code/expiration columns and methods in data source

This commit is contained in:
ljacqu
2016-09-10 09:13:17 +02:00
parent ffc5b77f36
commit 0aac8928af
13 changed files with 243 additions and 2 deletions
@@ -235,6 +235,24 @@ public class CacheDataSource implements DataSource {
return source.getAllAuths();
}
@Override
public void setRecoveryCode(String name, String code, long expiration) {
source.setRecoveryCode(name, code, expiration);
cachedAuths.refresh(name);
}
@Override
public String getRecoveryCode(String name) {
// TODO #472: can probably get it from the cached Auth?
return source.getRecoveryCode(name);
}
@Override
public void removeRecoveryCode(String name) {
source.removeRecoveryCode(name);
cachedAuths.refresh(name);
}
@Override
public List<PlayerAuth> getLoggedPlayers() {
return new ArrayList<>(PlayerCache.getInstance().getCache().values());
@@ -22,6 +22,8 @@ public final class Columns {
public final String EMAIL;
public final String ID;
public final String IS_LOGGED;
public final String RECOVERY_CODE;
public final String RECOVERY_EXPIRATION;
public Columns(Settings settings) {
NAME = settings.getProperty(DatabaseSettings.MYSQL_COL_NAME);
@@ -38,6 +40,8 @@ public final class Columns {
EMAIL = settings.getProperty(DatabaseSettings.MYSQL_COL_EMAIL);
ID = settings.getProperty(DatabaseSettings.MYSQL_COL_ID);
IS_LOGGED = settings.getProperty(DatabaseSettings.MYSQL_COL_ISLOGGED);
RECOVERY_CODE = settings.getProperty(DatabaseSettings.MYSQL_COL_RECOVERY_CODE);
RECOVERY_EXPIRATION = settings.getProperty(DatabaseSettings.MYSQL_COL_RECOVERY_EXPIRATION);
}
}
@@ -194,6 +194,30 @@ public interface DataSource extends Reloadable {
*/
List<PlayerAuth> getAllAuths();
/**
* Set the password recovery code for a user.
*
* @param name The name of the user
* @param code The recovery code
* @param expiration Recovery code expiration (milliseconds timestamp)
*/
void setRecoveryCode(String name, String code, long expiration);
/**
* Get the recovery code of a user if available and not yet expired.
*
* @param name The name of the user
* @return The recovery code, or null if no current code available
*/
String getRecoveryCode(String name);
/**
* Remove the recovery code of a given user.
*
* @param name The name of the user
*/
void removeRecoveryCode(String name);
/**
* Reload the data source.
*/
@@ -468,6 +468,21 @@ public class FlatFile implements DataSource {
throw new UnsupportedOperationException("Flat file no longer supported");
}
@Override
public void setRecoveryCode(String name, String code, long expiration) {
throw new UnsupportedOperationException("Flat file no longer supported");
}
@Override
public String getRecoveryCode(String name) {
throw new UnsupportedOperationException("Flat file no longer supported");
}
@Override
public void removeRecoveryCode(String name) {
throw new UnsupportedOperationException("Flat file no longer supported");
}
private static PlayerAuth buildAuthFromArray(String[] args) {
// Format allows 2, 3, 4, 7, 8, 9 fields. Anything else is unknown
if (args.length >= 2 && args.length <= 9 && args.length != 5 && args.length != 6) {
@@ -208,6 +208,14 @@ public class MySQL implements DataSource {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN "
+ col.IS_LOGGED + " SMALLINT NOT NULL DEFAULT '0' AFTER " + col.EMAIL);
}
if (isColumnMissing(md, col.RECOVERY_CODE)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.RECOVERY_CODE + " VARCHAR(20);");
}
if (isColumnMissing(md, col.RECOVERY_EXPIRATION)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.RECOVERY_EXPIRATION + " BIGINT;");
}
}
ConsoleLogger.info("MySQL setup finished");
}
@@ -856,6 +864,54 @@ public class MySQL implements DataSource {
return auths;
}
@Override
public void setRecoveryCode(String name, String code, long expiration) {
String sql = "UPDATE " + tableName
+ " SET " + col.RECOVERY_CODE + " = ?, "
+ col.RECOVERY_EXPIRATION + " = ?"
+ " WHERE " + col.NAME + " = ?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, code);
pst.setLong(2, expiration);
pst.setString(3, name.toLowerCase());
pst.executeUpdate();
} catch (SQLException e) {
logSqlException(e);
}
}
@Override
public String getRecoveryCode(String name) {
String sql = "SELECT " + col.RECOVERY_CODE + " FROM " + tableName
+ " WHERE " + col.NAME + " = ? AND " + col.RECOVERY_EXPIRATION + " > ?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, name.toLowerCase());
pst.setLong(2, System.currentTimeMillis());
try (ResultSet rs = pst.executeQuery()) {
if (rs.next()) {
return rs.getString(1);
}
}
} catch (SQLException e) {
logSqlException(e);
}
return null;
}
@Override
public void removeRecoveryCode(String name) {
String sql = "UPDATE " + tableName
+ " SET " + col.RECOVERY_CODE + " = NULL"
+ " AND " + col.RECOVERY_EXPIRATION + " = NULL"
+ " WHERE " + col.NAME + " = ?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, name.toLowerCase());
pst.executeUpdate();
} catch (SQLException e) {
logSqlException(e);
}
}
private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException {
String salt = col.SALT.isEmpty() ? null : row.getString(col.SALT);
int group = col.GROUP.isEmpty() ? -1 : row.getInt(col.GROUP);
@@ -127,6 +127,14 @@ public class SQLite implements DataSource {
if (isColumnMissing(md, col.IS_LOGGED)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.IS_LOGGED + " INT DEFAULT '0';");
}
if (isColumnMissing(md, col.RECOVERY_CODE)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.RECOVERY_CODE + " VARCHAR(20);");
}
if (isColumnMissing(md, col.RECOVERY_EXPIRATION)) {
st.executeUpdate("ALTER TABLE " + tableName + " ADD COLUMN " + col.RECOVERY_EXPIRATION + " BIGINT;");
}
}
ConsoleLogger.info("SQLite Setup finished");
}
@@ -586,6 +594,54 @@ public class SQLite implements DataSource {
return auths;
}
@Override
public void setRecoveryCode(String name, String code, long expiration) {
String sql = "UPDATE " + tableName
+ " SET " + col.RECOVERY_CODE + " = ?, "
+ col.RECOVERY_EXPIRATION + " = ?"
+ " WHERE " + col.NAME + " = ?;";
try (PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, code);
pst.setLong(2, expiration);
pst.setString(3, name.toLowerCase());
pst.executeUpdate();
} catch (SQLException e) {
logSqlException(e);
}
}
@Override
public String getRecoveryCode(String name) {
String sql = "SELECT " + col.RECOVERY_CODE + " FROM " + tableName
+ " WHERE " + col.NAME + " = ? AND " + col.RECOVERY_EXPIRATION + " > ?;";
try (PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, name.toLowerCase());
pst.setLong(2, System.currentTimeMillis());
try (ResultSet rs = pst.executeQuery()) {
if (rs.next()) {
return rs.getString(1);
}
}
} catch (SQLException e) {
logSqlException(e);
}
return null;
}
@Override
public void removeRecoveryCode(String name) {
String sql = "UPDATE " + tableName
+ " SET " + col.RECOVERY_CODE + " = NULL"
+ " AND " + col.RECOVERY_EXPIRATION + " = NULL"
+ " WHERE " + col.NAME + " = ?;";
try (PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, name.toLowerCase());
pst.executeUpdate();
} catch (SQLException e) {
logSqlException(e);
}
}
private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException {
String salt = !col.SALT.isEmpty() ? row.getString(col.SALT) : null;
@@ -98,6 +98,14 @@ public class DatabaseSettings implements SettingsHolder {
public static final Property<String> MYSQL_COL_GROUP =
newProperty("ExternalBoardOptions.mySQLColumnGroup", "");
@Comment("Column for storing recovery code (when password lost)")
public static final Property<String> MYSQL_COL_RECOVERY_CODE =
newProperty("DataSource.mySQLrecoveryCode", "recoverycode");
@Comment("Column for storing recovery code expiration")
public static final Property<String> MYSQL_COL_RECOVERY_EXPIRATION =
newProperty("DataSource.mySQLrecoveryExpiration", "recoveryexpiration");
private DatabaseSettings() {
}