From aed23cb1efec6cff8aaf205020c04af7ecffe022 Mon Sep 17 00:00:00 2001
From: ljacqu
Date: Thu, 31 Dec 2015 00:36:08 +0100
Subject: [PATCH 1/9] Revert removal of XENFORO enum, hash class and custom SQL
- Undo commits 121d323 and 1c12278 - Add TODO's with issue number - Add
slight, necessary adjustments for code changes since the reverted commits
---
.../fr/xephi/authme/datasource/MySQL.java | 104 +++++++++++++++++-
.../xephi/authme/security/HashAlgorithm.java | 1 +
.../fr/xephi/authme/security/crypts/XF.java | 74 +++++++++++++
src/main/resources/config.yml | 2 +-
.../HashAlgorithmIntegrationTest.java | 6 +
.../xephi/authme/security/crypts/XFTest.java | 19 ++++
6 files changed, 204 insertions(+), 2 deletions(-)
create mode 100644 src/main/java/fr/xephi/authme/security/crypts/XF.java
create mode 100644 src/test/java/fr/xephi/authme/security/crypts/XFTest.java
diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java
index 1af04cab5..e0c919553 100644
--- a/src/main/java/fr/xephi/authme/datasource/MySQL.java
+++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java
@@ -257,6 +257,7 @@ public class MySQL implements DataSource {
}
String salt = !columnSalt.isEmpty() ? rs.getString(columnSalt) : null;
int group = !columnGroup.isEmpty() ? rs.getInt(columnGroup) : -1;
+ int id = rs.getInt(columnID);
pAuth = PlayerAuth.builder()
.name(rs.getString(columnName))
.realName(rs.getString(columnRealName))
@@ -272,6 +273,17 @@ public class MySQL implements DataSource {
.build();
rs.close();
pst.close();
+ if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
+ pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + columnID + "=?;");
+ pst.setInt(1, id);
+ rs = pst.executeQuery();
+ if (rs.next()) {
+ Blob blob = rs.getBlob("data");
+ byte[] bytes = blob.getBytes(1, (int) blob.length());
+ // TODO #137: Need to find out how the salt is loaded and need to pass it along to setHash()
+ // pAuth.setPassword(new String(bytes));
+ }
+ }
} catch (SQLException ex) {
ConsoleLogger.showError(ex.getMessage());
ConsoleLogger.writeStackTrace(ex);
@@ -452,6 +464,26 @@ public class MySQL implements DataSource {
}
rs.close();
pst.close();
+ } else if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
+ pst = con.prepareStatement("SELECT " + columnID + " FROM " + tableName + " WHERE " + columnName + "=?;");
+ pst.setString(1, auth.getNickname());
+ rs = pst.executeQuery();
+ if (rs.next()) {
+ int id = rs.getInt(columnID);
+ // Insert password in the correct table
+ pst2 = con.prepareStatement("INSERT INTO xf_user_authenticate (user_id, scheme_class, data) VALUES (?,?,?);");
+ pst2.setInt(1, id);
+ pst2.setString(2, "XenForo_Authentication_Core12");
+ // TODO #137: Need to verify that the salt info is also being passed on...
+ byte[] bytes = auth.getPassword().getHash().getBytes();
+ Blob blob = con.createBlob();
+ blob.setBytes(1, bytes);
+ pst2.setBlob(3, blob);
+ pst2.executeUpdate();
+ pst2.close();
+ }
+ rs.close();
+ pst.close();
}
return true;
} catch (SQLException ex) {
@@ -482,6 +514,35 @@ public class MySQL implements DataSource {
}
pst.executeUpdate();
pst.close();
+ if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
+ String sql = "SELECT " + columnID + " FROM " + tableName + " WHERE " + columnName + "=?;";
+ pst = con.prepareStatement(sql);
+ pst.setString(1, auth.getNickname());
+ ResultSet rs = pst.executeQuery();
+ if (rs.next()) {
+ int id = rs.getInt(columnID);
+ // Insert password in the correct table
+ sql = "UPDATE xf_user_authenticate SET data=? WHERE " + columnID + "=?;";
+ PreparedStatement pst2 = con.prepareStatement(sql);
+ // TODO #137: What about the salt?
+ byte[] bytes = auth.getPassword().getHash().getBytes();
+ Blob blob = con.createBlob();
+ blob.setBytes(1, bytes);
+ pst2.setBlob(1, blob);
+ pst2.setInt(2, id);
+ pst2.executeUpdate();
+ pst2.close();
+ // ...
+ sql = "UPDATE xf_user_authenticate SET scheme_class=? WHERE " + columnID + "=?;";
+ pst2 = con.prepareStatement(sql);
+ pst2.setString(1, "XenForo_Authentication_Core12");
+ pst2.setInt(2, id);
+ pst2.executeUpdate();
+ pst2.close();
+ }
+ rs.close();
+ pst.close();
+ }
return true;
} catch (SQLException ex) {
ConsoleLogger.showError(ex.getMessage());
@@ -549,7 +610,24 @@ public class MySQL implements DataSource {
public synchronized boolean removeAuth(String user) {
user = user.toLowerCase();
try (Connection con = getConnection()) {
- PreparedStatement pst = con.prepareStatement("DELETE FROM " + tableName + " WHERE " + columnName + "=?;");
+ String sql;
+ PreparedStatement pst;
+ if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
+ sql = "SELECT " + columnID + " FROM " + tableName + " WHERE " + columnName + "=?;";
+ pst = con.prepareStatement(sql);
+ pst.setString(1, user);
+ ResultSet rs = pst.executeQuery();
+ if (rs.next()) {
+ int id = rs.getInt(columnID);
+ sql = "DELETE FROM xf_user_authenticate WHERE " + columnID + "=" + id;
+ Statement st = con.createStatement();
+ st.executeUpdate(sql);
+ st.close();
+ }
+ rs.close();
+ pst.close();
+ }
+ pst = con.prepareStatement("DELETE FROM " + tableName + " WHERE " + columnName + "=?;");
pst.setString(1, user);
pst.executeUpdate();
return true;
@@ -835,6 +913,18 @@ public class MySQL implements DataSource {
.groupId(group)
.build();
+ if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
+ int id = rs.getInt(columnID);
+ pst.setInt(1, id);
+ ResultSet rs2 = pst.executeQuery();
+ if (rs2.next()) {
+ Blob blob = rs2.getBlob("data");
+ byte[] bytes = blob.getBytes(1, (int) blob.length());
+ // TODO #137: Need to pass the hash and the salt here
+ // pAuth.setPassword(new String(bytes));
+ }
+ rs2.close();
+ }
auths.add(pAuth);
}
pst.close();
@@ -871,6 +961,18 @@ public class MySQL implements DataSource {
.groupId(group)
.build();
+ if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
+ int id = rs.getInt(columnID);
+ pst.setInt(1, id);
+ ResultSet rs2 = pst.executeQuery();
+ if (rs2.next()) {
+ Blob blob = rs2.getBlob("data");
+ byte[] bytes = blob.getBytes(1, (int) blob.length());
+ // TODO #137: Need to pass the hash and the salt here
+ // pAuth.setHash(new String(bytes));
+ }
+ rs2.close();
+ }
auths.add(pAuth);
}
} catch (Exception ex) {
diff --git a/src/main/java/fr/xephi/authme/security/HashAlgorithm.java b/src/main/java/fr/xephi/authme/security/HashAlgorithm.java
index 24a702dd0..50572eb61 100644
--- a/src/main/java/fr/xephi/authme/security/HashAlgorithm.java
+++ b/src/main/java/fr/xephi/authme/security/HashAlgorithm.java
@@ -36,6 +36,7 @@ public enum HashAlgorithm {
WHIRLPOOL(fr.xephi.authme.security.crypts.WHIRLPOOL.class),
WORDPRESS(fr.xephi.authme.security.crypts.WORDPRESS.class),
XAUTH(fr.xephi.authme.security.crypts.XAUTH.class),
+ XENFORO(fr.xephi.authme.security.crypts.XF.class),
CUSTOM(null);
private final Class extends EncryptionMethod> clazz;
diff --git a/src/main/java/fr/xephi/authme/security/crypts/XF.java b/src/main/java/fr/xephi/authme/security/crypts/XF.java
new file mode 100644
index 000000000..2a74d2faa
--- /dev/null
+++ b/src/main/java/fr/xephi/authme/security/crypts/XF.java
@@ -0,0 +1,74 @@
+package fr.xephi.authme.security.crypts;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class XF implements EncryptionMethod {
+
+ @Override
+ public String computeHash(String password, String salt, String name) {
+ return getSha256(getSha256(password) + regmatch("\"salt\";.:..:\"(.*)\";.:.:\"hashFunc\"", salt));
+ }
+
+ @Override
+ public HashedPassword computeHash(String password, String name) {
+ String salt = generateSalt();
+ return new HashedPassword(computeHash(password, salt, null), salt);
+ }
+
+ @Override
+ public boolean comparePassword(String password, HashedPassword hashedPassword, String name) {
+ // TODO #137: Write the comparePassword method. See commit 121d323 for what was here previously; it was
+ // utter non-sense
+ return false;
+ }
+
+ // TODO #137: If this method corresponds to HashUtils.sha256(), use it instead of this
+ private String getSha256(String password) {
+ MessageDigest md = null;
+ try {
+ md = MessageDigest.getInstance("SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ md.update(password.getBytes());
+ byte byteData[] = md.digest();
+ StringBuilder sb = new StringBuilder();
+ for (byte element : byteData) {
+ sb.append(Integer.toString((element & 0xff) + 0x100, 16).substring(1));
+ }
+ StringBuilder hexString = new StringBuilder();
+ for (byte element : byteData) {
+ String hex = Integer.toHexString(0xff & element);
+ if (hex.length() == 1) {
+ hexString.append('0');
+ }
+ hexString.append(hex);
+ }
+ return hexString.toString();
+ }
+
+ @Override
+ public String generateSalt() {
+ // TODO #137: Find out what kind of salt format XF uses to generate new passwords
+ return "";
+ }
+
+ @Override
+ public boolean hasSeparateSalt() {
+ return true;
+ }
+
+ private String regmatch(String pattern, String line) {
+ List allMatches = new ArrayList<>();
+ Matcher m = Pattern.compile(pattern).matcher(line);
+ while (m.find()) {
+ allMatches.add(m.group(1));
+ }
+ return allMatches.get(0);
+ }
+}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index a98d97393..7f151c495 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -181,7 +181,7 @@ settings:
# Example unLoggedinGroup: NotLogged
unLoggedinGroup: unLoggedinGroup
# possible values: MD5, SHA1, SHA256, WHIRLPOOL, XAUTH, MD5VB, PHPBB,
- # MYBB, IPB3, PHPFUSION, SMF, SALTED2MD5, JOOMLA, BCRYPT, WBB3, SHA512,
+ # MYBB, IPB3, PHPFUSION, SMF, XENFORO, SALTED2MD5, JOOMLA, BCRYPT, WBB3, SHA512,
# DOUBLEMD5, PBKDF2, PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM(for developpers only)
passwordHash: SHA256
# salt length for the SALTED2MD5 MD5(MD5(password)+salt)
diff --git a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java
index 2472c6592..4583940e2 100644
--- a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java
+++ b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java
@@ -47,6 +47,12 @@ public class HashAlgorithmIntegrationTest {
public void shouldBeAbleToInstantiateEncryptionAlgorithms() throws InstantiationException, IllegalAccessException {
// given / when / then
for (HashAlgorithm algorithm : HashAlgorithm.values()) {
+ // TODO #137: Remove this check
+ if (HashAlgorithm.XENFORO.equals(algorithm)) {
+ System.out.println("Note: Currently skipping XENFORO hash (TODO #137: Fix it)");
+ continue;
+ }
+
if (!HashAlgorithm.CUSTOM.equals(algorithm)) {
EncryptionMethod method = algorithm.getClazz().newInstance();
HashedPassword hashedPassword = method.computeHash("pwd", "name");
diff --git a/src/test/java/fr/xephi/authme/security/crypts/XFTest.java b/src/test/java/fr/xephi/authme/security/crypts/XFTest.java
new file mode 100644
index 000000000..d9226baca
--- /dev/null
+++ b/src/test/java/fr/xephi/authme/security/crypts/XFTest.java
@@ -0,0 +1,19 @@
+package fr.xephi.authme.security.crypts;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Test for {@link XF}.
+ */
+@Ignore
+// TODO #137: Create a test class as for the other encryption methods. Simply run the following test and copy the
+// output -- that's your test class! (Once XF.java actually works properly)
+// @org.junit.Test public void a() { AbstractEncryptionMethodTest.generateTest(new XF()); }
+public class XFTest {
+
+ @Test
+ public void shouldComputeHash() {
+ System.out.println(new XF().computeHash("Test", "name"));
+ }
+}
From bd5d341e67a6f8ef222d0c25d17da8c05954fd20 Mon Sep 17 00:00:00 2001
From: DNx5
Date: Thu, 31 Dec 2015 11:05:18 +0700
Subject: [PATCH 2/9] Xenforo support. - Added getPassword method in DataSource
and all implementations.
---
.../authme/datasource/CacheDataSource.java | 11 +++
.../xephi/authme/datasource/DataSource.java | 10 +++
.../fr/xephi/authme/datasource/FlatFile.java | 10 +++
.../fr/xephi/authme/datasource/MySQL.java | 43 +++++++++---
.../fr/xephi/authme/datasource/SQLite.java | 30 +++++++-
.../authme/security/PasswordSecurity.java | 8 +--
.../xephi/authme/security/crypts/BCRYPT.java | 40 ++++++-----
.../security/crypts/HashedPassword.java | 8 +--
.../fr/xephi/authme/security/crypts/XF.java | 70 +++----------------
9 files changed, 130 insertions(+), 100 deletions(-)
diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java
index 2351c6cb9..2887b9ee7 100644
--- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java
+++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java
@@ -9,6 +9,7 @@ import com.google.common.cache.RemovalListeners;
import com.google.common.cache.RemovalNotification;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
+import fr.xephi.authme.security.crypts.HashedPassword;
import java.util.ArrayList;
import java.util.List;
@@ -65,6 +66,16 @@ public class CacheDataSource implements DataSource {
return getAuth(user) != null;
}
+ @Override
+ public HashedPassword getPassword(String user) {
+ user = user.toLowerCase();
+ Optional pAuthOpt = cachedAuths.getIfPresent(user);
+ if (pAuthOpt != null && pAuthOpt.isPresent()) {
+ return pAuthOpt.get().getPassword();
+ }
+ return source.getPassword(user);
+ }
+
/**
* Method getAuth.
*
diff --git a/src/main/java/fr/xephi/authme/datasource/DataSource.java b/src/main/java/fr/xephi/authme/datasource/DataSource.java
index 10f74b929..9e5f051ab 100644
--- a/src/main/java/fr/xephi/authme/datasource/DataSource.java
+++ b/src/main/java/fr/xephi/authme/datasource/DataSource.java
@@ -1,6 +1,7 @@
package fr.xephi.authme.datasource;
import fr.xephi.authme.cache.auth.PlayerAuth;
+import fr.xephi.authme.security.crypts.HashedPassword;
import java.util.List;
@@ -17,6 +18,15 @@ public interface DataSource {
*/
boolean isAuthAvailable(String user);
+ /**
+ * Method getPassword.
+ *
+ * @param user String
+ *
+ * @return String
+ */
+ HashedPassword getPassword(String user);
+
/**
* Method getAuth.
*
diff --git a/src/main/java/fr/xephi/authme/datasource/FlatFile.java b/src/main/java/fr/xephi/authme/datasource/FlatFile.java
index 55646ddc3..da18625b9 100644
--- a/src/main/java/fr/xephi/authme/datasource/FlatFile.java
+++ b/src/main/java/fr/xephi/authme/datasource/FlatFile.java
@@ -14,6 +14,7 @@ import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
+import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.Settings;
/**
@@ -87,6 +88,15 @@ public class FlatFile implements DataSource {
return false;
}
+ @Override
+ public HashedPassword getPassword(String user) {
+ PlayerAuth auth = getAuth(user);
+ if (auth != null) {
+ return auth.getPassword();
+ }
+ return null;
+ }
+
/**
* Method saveAuth.
*
diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java
index e0c919553..e3ce9b5f2 100644
--- a/src/main/java/fr/xephi/authme/datasource/MySQL.java
+++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java
@@ -6,10 +6,18 @@ import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.HashAlgorithm;
+import fr.xephi.authme.security.crypts.HashedPassword;
+import fr.xephi.authme.security.crypts.XF;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.StringUtils;
-import java.sql.*;
+import java.sql.Blob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
@@ -244,6 +252,25 @@ public class MySQL implements DataSource {
return false;
}
+ @Override
+ public HashedPassword getPassword(String user) {
+ try (Connection con = getConnection()) {
+ String sql = "SELECT " + columnPassword + "," + columnSalt + " FROM " + tableName
+ + " WHERE " + columnName + "=?;";
+ PreparedStatement pst = con.prepareStatement(sql);
+ pst.setString(1, user.toLowerCase());
+ ResultSet rs = pst.executeQuery();
+ if (rs.next()) {
+ return new HashedPassword(rs.getString(columnPassword),
+ !columnSalt.isEmpty() ? rs.getString(columnSalt) : null);
+ }
+ } catch (SQLException ex) {
+ ConsoleLogger.showError(ex.getMessage());
+ ConsoleLogger.writeStackTrace(ex);
+ }
+ return null;
+ }
+
@Override
public synchronized PlayerAuth getAuth(String user) {
PlayerAuth pAuth;
@@ -280,8 +307,7 @@ public class MySQL implements DataSource {
if (rs.next()) {
Blob blob = rs.getBlob("data");
byte[] bytes = blob.getBytes(1, (int) blob.length());
- // TODO #137: Need to find out how the salt is loaded and need to pass it along to setHash()
- // pAuth.setPassword(new String(bytes));
+ pAuth.setPassword(new HashedPassword(XF.getHashFromBlob(bytes)));
}
}
} catch (SQLException ex) {
@@ -470,11 +496,9 @@ public class MySQL implements DataSource {
rs = pst.executeQuery();
if (rs.next()) {
int id = rs.getInt(columnID);
- // Insert password in the correct table
pst2 = con.prepareStatement("INSERT INTO xf_user_authenticate (user_id, scheme_class, data) VALUES (?,?,?);");
pst2.setInt(1, id);
pst2.setString(2, "XenForo_Authentication_Core12");
- // TODO #137: Need to verify that the salt info is also being passed on...
byte[] bytes = auth.getPassword().getHash().getBytes();
Blob blob = con.createBlob();
blob.setBytes(1, bytes);
@@ -524,7 +548,6 @@ public class MySQL implements DataSource {
// Insert password in the correct table
sql = "UPDATE xf_user_authenticate SET data=? WHERE " + columnID + "=?;";
PreparedStatement pst2 = con.prepareStatement(sql);
- // TODO #137: What about the salt?
byte[] bytes = auth.getPassword().getHash().getBytes();
Blob blob = con.createBlob();
blob.setBytes(1, bytes);
@@ -757,7 +780,7 @@ public class MySQL implements DataSource {
}
@Override
- public synchronized List getAllAuthsByEmail(String email){
+ public synchronized List getAllAuthsByEmail(String email) {
List countEmail = new ArrayList<>();
try (Connection con = getConnection()) {
String sql = "SELECT " + columnName + " FROM " + tableName + " WHERE " + columnEmail + "=?;";
@@ -920,8 +943,7 @@ public class MySQL implements DataSource {
if (rs2.next()) {
Blob blob = rs2.getBlob("data");
byte[] bytes = blob.getBytes(1, (int) blob.length());
- // TODO #137: Need to pass the hash and the salt here
- // pAuth.setPassword(new String(bytes));
+ pAuth.setPassword(new HashedPassword(XF.getHashFromBlob(bytes)));
}
rs2.close();
}
@@ -968,8 +990,7 @@ public class MySQL implements DataSource {
if (rs2.next()) {
Blob blob = rs2.getBlob("data");
byte[] bytes = blob.getBytes(1, (int) blob.length());
- // TODO #137: Need to pass the hash and the salt here
- // pAuth.setHash(new String(bytes));
+ pAuth.setPassword(new HashedPassword(XF.getHashFromBlob(bytes)));
}
rs2.close();
}
diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java
index 474c5035f..1eef222fa 100644
--- a/src/main/java/fr/xephi/authme/datasource/SQLite.java
+++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java
@@ -6,7 +6,12 @@ import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.StringUtils;
-import java.sql.*;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
@@ -167,6 +172,29 @@ public class SQLite implements DataSource {
}
}
+ @Override
+ public HashedPassword getPassword(String user) {
+ PreparedStatement pst = null;
+ ResultSet rs = null;
+ try {
+ pst = con.prepareStatement("SELECT " + columnPassword + "," + columnSalt
+ + " FROM " + tableName + " WHERE " + columnName + "=?");
+ pst.setString(1, user);
+ rs = pst.executeQuery();
+ if (rs.next()) {
+ return new HashedPassword(rs.getString(columnPassword),
+ !columnSalt.isEmpty() ? rs.getString(columnSalt) : null);
+ }
+ } catch (SQLException ex) {
+ ConsoleLogger.showError(ex.getMessage());
+ ConsoleLogger.writeStackTrace(ex);
+ } finally {
+ close(rs);
+ close(pst);
+ }
+ return null;
+ }
+
/**
* Method getAuth.
*
diff --git a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java
index 56ed1e4cd..75a1fcdc3 100644
--- a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java
+++ b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java
@@ -36,12 +36,8 @@ public class PasswordSecurity {
}
public boolean comparePassword(String password, String playerName) {
- // TODO ljacqu 20151230: Defining a dataSource.getPassword() method would be more efficient
- PlayerAuth auth = dataSource.getAuth(playerName);
- if (auth != null) {
- return comparePassword(password, auth.getPassword(), playerName);
- }
- return false;
+ HashedPassword auth = dataSource.getPassword(playerName);
+ return auth != null && comparePassword(password, auth, playerName);
}
public boolean comparePassword(String password, HashedPassword hashedPassword, String playerName) {
diff --git a/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java b/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java
index b7602e7fe..eb8c717a9 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java
@@ -15,9 +15,9 @@ package fr.xephi.authme.security.crypts;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.security.crypts.description.HasSalt;
-import fr.xephi.authme.security.crypts.description.Usage;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
+import fr.xephi.authme.security.crypts.description.Usage;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.StringUtils;
@@ -25,43 +25,45 @@ import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
/**
+ *
* BCrypt implements OpenBSD-style Blowfish password hashing using the scheme
* described in "A Future-Adaptable Password Scheme" by Niels Provos and David
* Mazieres.
- *
+ *
* This password hashing system tries to thwart off-line password cracking using
* a computationally-intensive hashing algorithm, based on Bruce Schneier's
* Blowfish cipher. The work factor of the algorithm is parameterised, so it can
* be increased as computers get faster.
- *
+ *
* Usage is really simple. To hash a password for the first time, call the
* hashpw method with a random salt, like this:
- *
+ *
*
- * String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt());
+ * String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt());
*
- *
+ *
* To check whether a plaintext password matches one that has been hashed
* previously, use the checkpw method:
- *
+ *
*
- * if (BCrypt.checkpw(candidate_password, stored_hash))
- * System.out.println("It matches");
- * else
- * System.out.println("It does not match");
+ * if (BCrypt.checkpw(candidate_password, stored_hash))
+ * System.out.println("It matches");
+ * else
+ * System.out.println("It does not match");
*
- *
+ *
* The gensalt() method takes an optional parameter (log_rounds) that determines
* the computational complexity of the hashing:
- *
+ *
*
- * String strong_salt = BCrypt.gensalt(10)
- * String stronger_salt = BCrypt.gensalt(12)
+ * String strong_salt = BCrypt.gensalt(10)
+ * String stronger_salt = BCrypt.gensalt(12)
*
- *
+ *
* The amount of work increases exponentially (2**log_rounds), so each increment
* is twice as much work. The default log_rounds is 10, and the valid range is 4
* to 31.
+ *
*
* @author Damien Miller
* @version 0.2
@@ -102,8 +104,11 @@ public class BCRYPT implements EncryptionMethod {
* @param d the byte array to encode
* @param len the number of bytes to encode
*
- * @return base64-encoded string * @throws IllegalArgumentException if the length is invalid * @throws IllegalArgumentException
+ * @return base64-encoded string
+ *
+ * @throws IllegalArgumentException if the length is invalid
*/
+
private static String encode_base64(byte d[], int len)
throws IllegalArgumentException {
int off = 0;
@@ -160,6 +165,7 @@ public class BCRYPT implements EncryptionMethod {
* @param maxolen the maximum number of bytes to decode
*
* @return an array containing the decoded bytes
+ *
* @throws IllegalArgumentException if maxolen is invalid
*/
private static byte[] decode_base64(String s, int maxolen)
diff --git a/src/main/java/fr/xephi/authme/security/crypts/HashedPassword.java b/src/main/java/fr/xephi/authme/security/crypts/HashedPassword.java
index ab1a7eebc..f2e40aa4e 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/HashedPassword.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/HashedPassword.java
@@ -4,7 +4,7 @@ package fr.xephi.authme.security.crypts;
* The result of a hash computation. See {@link #salt} for details.
*/
public class HashedPassword {
-
+
/** The generated hash. */
private final String hash;
/**
@@ -36,13 +36,13 @@ public class HashedPassword {
public HashedPassword(String hash) {
this(hash, null);
}
-
+
public String getHash() {
return hash;
}
-
+
public String getSalt() {
return salt;
}
-
+
}
diff --git a/src/main/java/fr/xephi/authme/security/crypts/XF.java b/src/main/java/fr/xephi/authme/security/crypts/XF.java
index 2a74d2faa..cd3bcf165 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/XF.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/XF.java
@@ -1,74 +1,22 @@
package fr.xephi.authme.security.crypts;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-public class XF implements EncryptionMethod {
-
- @Override
- public String computeHash(String password, String salt, String name) {
- return getSha256(getSha256(password) + regmatch("\"salt\";.:..:\"(.*)\";.:.:\"hashFunc\"", salt));
- }
-
- @Override
- public HashedPassword computeHash(String password, String name) {
- String salt = generateSalt();
- return new HashedPassword(computeHash(password, salt, null), salt);
- }
-
- @Override
- public boolean comparePassword(String password, HashedPassword hashedPassword, String name) {
- // TODO #137: Write the comparePassword method. See commit 121d323 for what was here previously; it was
- // utter non-sense
- return false;
- }
-
- // TODO #137: If this method corresponds to HashUtils.sha256(), use it instead of this
- private String getSha256(String password) {
- MessageDigest md = null;
- try {
- md = MessageDigest.getInstance("SHA-256");
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- md.update(password.getBytes());
- byte byteData[] = md.digest();
- StringBuilder sb = new StringBuilder();
- for (byte element : byteData) {
- sb.append(Integer.toString((element & 0xff) + 0x100, 16).substring(1));
- }
- StringBuilder hexString = new StringBuilder();
- for (byte element : byteData) {
- String hex = Integer.toHexString(0xff & element);
- if (hex.length() == 1) {
- hexString.append('0');
- }
- hexString.append(hex);
- }
- return hexString.toString();
- }
+public class XF extends BCRYPT {
+ private static final Pattern HASH_PATTERN = Pattern.compile("\"hash\";s.*\"(.*)?\"");
@Override
public String generateSalt() {
- // TODO #137: Find out what kind of salt format XF uses to generate new passwords
- return "";
+ return BCRYPT.gensalt();
}
- @Override
- public boolean hasSeparateSalt() {
- return true;
- }
-
- private String regmatch(String pattern, String line) {
- List allMatches = new ArrayList<>();
- Matcher m = Pattern.compile(pattern).matcher(line);
- while (m.find()) {
- allMatches.add(m.group(1));
+ public static String getHashFromBlob(byte[] blob) {
+ String line = new String(blob);
+ Matcher m = HASH_PATTERN.matcher(line);
+ if (m.find()) {
+ return m.group(1);
}
- return allMatches.get(0);
+ return "*"; // what?
}
}
From 9eeb510b084ba6dd9fc029f7398d1120dbdc7682 Mon Sep 17 00:00:00 2001
From: DNx5
Date: Thu, 31 Dec 2015 12:20:48 +0700
Subject: [PATCH 3/9] Messing up the test.
---
.../authme/security/PasswordSecurityTest.java | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java
index 8c1edffd5..f18a05707 100644
--- a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java
+++ b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java
@@ -70,9 +70,7 @@ public class PasswordSecurityTest {
String playerLowerCase = playerName.toLowerCase();
String clearTextPass = "myPassTest";
- PlayerAuth auth = mock(PlayerAuth.class);
- given(auth.getPassword()).willReturn(password);
- given(dataSource.getAuth(playerName)).willReturn(auth);
+ given(dataSource.getPassword(playerName)).willReturn(password);
given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(true);
PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.BCRYPT, pluginManager, false);
@@ -81,7 +79,7 @@ public class PasswordSecurityTest {
// then
assertThat(result, equalTo(true));
- verify(dataSource).getAuth(playerName);
+ verify(dataSource).getPassword(playerName);
verify(pluginManager).callEvent(any(PasswordEncryptionEvent.class));
verify(method).comparePassword(clearTextPass, password, playerLowerCase);
}
@@ -95,8 +93,7 @@ public class PasswordSecurityTest {
String clearTextPass = "passw0Rd1";
PlayerAuth auth = mock(PlayerAuth.class);
- given(auth.getPassword()).willReturn(password);
- given(dataSource.getAuth(playerName)).willReturn(auth);
+ given(dataSource.getPassword(playerName)).willReturn(password);
given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(false);
PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.CUSTOM, pluginManager, false);
@@ -105,7 +102,7 @@ public class PasswordSecurityTest {
// then
assertThat(result, equalTo(false));
- verify(dataSource).getAuth(playerName);
+ verify(dataSource).getPassword(playerName);
verify(pluginManager).callEvent(any(PasswordEncryptionEvent.class));
verify(method).comparePassword(clearTextPass, password, playerLowerCase);
}
@@ -116,7 +113,7 @@ public class PasswordSecurityTest {
String playerName = "bobby";
String clearTextPass = "tables";
- given(dataSource.getAuth(playerName)).willReturn(null);
+ given(dataSource.getPassword(playerName)).willReturn(null);
PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.MD5, pluginManager, false);
// when
@@ -124,7 +121,7 @@ public class PasswordSecurityTest {
// then
assertThat(result, equalTo(false));
- verify(dataSource).getAuth(playerName);
+ verify(dataSource).getPassword(playerName);
verify(pluginManager, never()).callEvent(any(Event.class));
verify(method, never()).comparePassword(anyString(), any(HashedPassword.class), anyString());
}
From 0c305a6287995265ee4951eda4a7a7d87f741239 Mon Sep 17 00:00:00 2001
From: DNx5
Date: Thu, 31 Dec 2015 12:55:34 +0700
Subject: [PATCH 4/9] Added new updatePassword method in DataSource class
---
.../authme/datasource/CacheDataSource.java | 10 ++++++
.../xephi/authme/datasource/DataSource.java | 2 ++
.../fr/xephi/authme/datasource/FlatFile.java | 36 +++++++++++--------
.../fr/xephi/authme/datasource/MySQL.java | 20 +++++++----
.../fr/xephi/authme/datasource/SQLite.java | 11 ++++--
.../authme/security/PasswordSecurity.java | 22 ++++++------
.../authme/security/PasswordSecurityTest.java | 2 ++
7 files changed, 66 insertions(+), 37 deletions(-)
diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java
index 2887b9ee7..50c833538 100644
--- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java
+++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java
@@ -127,6 +127,16 @@ public class CacheDataSource implements DataSource {
return result;
}
+ @Override
+ public boolean updatePassword(String user, HashedPassword password) {
+ user = user.toLowerCase();
+ boolean result = source.updatePassword(user, password);
+ if (result) {
+ cachedAuths.refresh(user);
+ }
+ return result;
+ }
+
/**
* Method updateSession.
*
diff --git a/src/main/java/fr/xephi/authme/datasource/DataSource.java b/src/main/java/fr/xephi/authme/datasource/DataSource.java
index 9e5f051ab..babba2ccc 100644
--- a/src/main/java/fr/xephi/authme/datasource/DataSource.java
+++ b/src/main/java/fr/xephi/authme/datasource/DataSource.java
@@ -63,6 +63,8 @@ public interface DataSource {
*/
boolean updatePassword(PlayerAuth auth);
+ boolean updatePassword(String user, HashedPassword password);
+
/**
* Method purgeDatabase.
*
diff --git a/src/main/java/fr/xephi/authme/datasource/FlatFile.java b/src/main/java/fr/xephi/authme/datasource/FlatFile.java
index da18625b9..b84a84c92 100644
--- a/src/main/java/fr/xephi/authme/datasource/FlatFile.java
+++ b/src/main/java/fr/xephi/authme/datasource/FlatFile.java
@@ -1,5 +1,12 @@
package fr.xephi.authme.datasource;
+import fr.xephi.authme.AuthMe;
+import fr.xephi.authme.ConsoleLogger;
+import fr.xephi.authme.cache.auth.PlayerAuth;
+import fr.xephi.authme.cache.auth.PlayerCache;
+import fr.xephi.authme.security.crypts.HashedPassword;
+import fr.xephi.authme.settings.Settings;
+
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
@@ -10,13 +17,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import fr.xephi.authme.AuthMe;
-import fr.xephi.authme.ConsoleLogger;
-import fr.xephi.authme.cache.auth.PlayerAuth;
-import fr.xephi.authme.cache.auth.PlayerCache;
-import fr.xephi.authme.security.crypts.HashedPassword;
-import fr.xephi.authme.settings.Settings;
-
/**
*/
@Deprecated
@@ -136,7 +136,13 @@ public class FlatFile implements DataSource {
*/
@Override
public synchronized boolean updatePassword(PlayerAuth auth) {
- if (!isAuthAvailable(auth.getNickname())) {
+ return updatePassword(auth.getNickname(), auth.getPassword());
+ }
+
+ @Override
+ public boolean updatePassword(String user, HashedPassword password) {
+ user = user.toLowerCase();
+ if (!isAuthAvailable(user)) {
return false;
}
PlayerAuth newAuth = null;
@@ -146,27 +152,27 @@ public class FlatFile implements DataSource {
String line;
while ((line = br.readLine()) != null) {
String[] args = line.split(":");
- if (args[0].equals(auth.getNickname())) {
+ if (args[0].equals(user)) {
// Note ljacqu 20151230: This does not persist the salt; it is not supported in flat file.
switch (args.length) {
case 4: {
- newAuth = new PlayerAuth(args[0], auth.getPassword().getHash(), args[2], Long.parseLong(args[3]), 0, 0, 0, "world", "your@email.com", args[0]);
+ newAuth = new PlayerAuth(args[0], password.getHash(), args[2], Long.parseLong(args[3]), 0, 0, 0, "world", "your@email.com", args[0]);
break;
}
case 7: {
- newAuth = new PlayerAuth(args[0], auth.getPassword().getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), "world", "your@email.com", args[0]);
+ newAuth = new PlayerAuth(args[0], password.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), "world", "your@email.com", args[0]);
break;
}
case 8: {
- newAuth = new PlayerAuth(args[0], auth.getPassword().getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], "your@email.com", args[0]);
+ newAuth = new PlayerAuth(args[0], password.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], "your@email.com", args[0]);
break;
}
case 9: {
- newAuth = new PlayerAuth(args[0], auth.getPassword().getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], args[8], args[0]);
+ newAuth = new PlayerAuth(args[0], password.getHash(), args[2], Long.parseLong(args[3]), Double.parseDouble(args[4]), Double.parseDouble(args[5]), Double.parseDouble(args[6]), args[7], args[8], args[0]);
break;
}
default: {
- newAuth = new PlayerAuth(args[0], auth.getPassword().getHash(), args[2], 0, 0, 0, 0, "world", "your@email.com", args[0]);
+ newAuth = new PlayerAuth(args[0], password.getHash(), args[2], 0, 0, 0, 0, "world", "your@email.com", args[0]);
break;
}
}
@@ -188,7 +194,7 @@ public class FlatFile implements DataSource {
}
}
if (newAuth != null) {
- removeAuth(auth.getNickname());
+ removeAuth(user);
saveAuth(newAuth);
}
return true;
diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java
index e3ce9b5f2..ebaf5d8d6 100644
--- a/src/main/java/fr/xephi/authme/datasource/MySQL.java
+++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java
@@ -519,6 +519,12 @@ public class MySQL implements DataSource {
@Override
public synchronized boolean updatePassword(PlayerAuth auth) {
+ return updatePassword(auth.getNickname(), auth.getPassword());
+ }
+
+ @Override
+ public boolean updatePassword(String user, HashedPassword password) {
+ user = user.toLowerCase();
try (Connection con = getConnection()) {
boolean useSalt = !columnSalt.isEmpty();
PreparedStatement pst;
@@ -526,29 +532,29 @@ public class MySQL implements DataSource {
String sql = String.format("UPDATE %s SET %s = ?, %s = ? WHERE %s = ?;",
tableName, columnPassword, columnSalt, columnName);
pst = con.prepareStatement(sql);
- pst.setString(1, auth.getPassword().getHash());
- pst.setString(2, auth.getPassword().getSalt());
- pst.setString(3, auth.getNickname());
+ pst.setString(1, password.getHash());
+ pst.setString(2, password.getSalt());
+ pst.setString(3, user);
} else {
String sql = String.format("UPDATE %s SET %s = ? WHERE %s = ?;",
tableName, columnPassword, columnName);
pst = con.prepareStatement(sql);
- pst.setString(1, auth.getPassword().getHash());
- pst.setString(2, auth.getNickname());
+ pst.setString(1, password.getHash());
+ pst.setString(2, user);
}
pst.executeUpdate();
pst.close();
if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
String sql = "SELECT " + columnID + " FROM " + tableName + " WHERE " + columnName + "=?;";
pst = con.prepareStatement(sql);
- pst.setString(1, auth.getNickname());
+ pst.setString(1, user);
ResultSet rs = pst.executeQuery();
if (rs.next()) {
int id = rs.getInt(columnID);
// Insert password in the correct table
sql = "UPDATE xf_user_authenticate SET data=? WHERE " + columnID + "=?;";
PreparedStatement pst2 = con.prepareStatement(sql);
- byte[] bytes = auth.getPassword().getHash().getBytes();
+ byte[] bytes = password.getHash().getBytes();
Blob blob = con.createBlob();
blob.setBytes(1, bytes);
pst2.setBlob(1, blob);
diff --git a/src/main/java/fr/xephi/authme/datasource/SQLite.java b/src/main/java/fr/xephi/authme/datasource/SQLite.java
index 1eef222fa..f3870db50 100644
--- a/src/main/java/fr/xephi/authme/datasource/SQLite.java
+++ b/src/main/java/fr/xephi/authme/datasource/SQLite.java
@@ -279,9 +279,14 @@ public class SQLite implements DataSource {
*/
@Override
public synchronized boolean updatePassword(PlayerAuth auth) {
+ return updatePassword(auth.getNickname(), auth.getPassword());
+ }
+
+ @Override
+ public boolean updatePassword(String user, HashedPassword password) {
+ user = user.toLowerCase();
PreparedStatement pst = null;
try {
- HashedPassword password = auth.getPassword();
boolean useSalt = !columnSalt.isEmpty();
String sql = "UPDATE " + tableName + " SET " + columnPassword + " = ?"
+ (useSalt ? ", " + columnSalt + " = ?" : "")
@@ -290,9 +295,9 @@ public class SQLite implements DataSource {
pst.setString(1, password.getHash());
if (useSalt) {
pst.setString(2, password.getSalt());
- pst.setString(3, auth.getNickname());
+ pst.setString(3, user);
} else {
- pst.setString(2, auth.getNickname());
+ pst.setString(2, user);
}
pst.executeUpdate();
} catch (SQLException ex) {
diff --git a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java
index 75a1fcdc3..552b64dd2 100644
--- a/src/main/java/fr/xephi/authme/security/PasswordSecurity.java
+++ b/src/main/java/fr/xephi/authme/security/PasswordSecurity.java
@@ -1,10 +1,9 @@
package fr.xephi.authme.security;
-import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.events.PasswordEncryptionEvent;
-import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.security.crypts.EncryptionMethod;
+import fr.xephi.authme.security.crypts.HashedPassword;
import org.bukkit.plugin.PluginManager;
/**
@@ -59,9 +58,10 @@ public class PasswordSecurity {
* the migration to a new encryption method. Upon a successful match, the password
* will be hashed with the new encryption method and persisted.
*
- * @param password The clear-text password to check
+ * @param password The clear-text password to check
* @param hashedPassword The encrypted password to test the clear-text password against
- * @param playerName The name of the player
+ * @param playerName The name of the player
+ *
* @return True if the
*/
private boolean compareWithAllEncryptionMethods(String password, HashedPassword hashedPassword,
@@ -83,8 +83,9 @@ public class PasswordSecurity {
* {@link PasswordEncryptionEvent}. The encryption method from the event is then returned,
* which may have been changed by an external listener.
*
- * @param algorithm The algorithm to retrieve the encryption method for
+ * @param algorithm The algorithm to retrieve the encryption method for
* @param playerName The name of the player a password will be hashed for
+ *
* @return The encryption method
*/
private EncryptionMethod initializeEncryptionMethod(HashAlgorithm algorithm, String playerName) {
@@ -98,6 +99,7 @@ public class PasswordSecurity {
* Initialize the encryption method corresponding to the given hash algorithm.
*
* @param algorithm The algorithm to retrieve the encryption method for
+ *
* @return The associated encryption method
*/
private static EncryptionMethod initializeEncryptionMethodWithoutEvent(HashAlgorithm algorithm) {
@@ -112,13 +114,9 @@ public class PasswordSecurity {
}
private void hashPasswordForNewAlgorithm(String password, String playerName) {
- PlayerAuth auth = dataSource.getAuth(playerName);
- if (auth != null) {
- HashedPassword hashedPassword = initializeEncryptionMethod(algorithm, playerName)
- .computeHash(password, playerName);
- auth.setPassword(hashedPassword);
- dataSource.updatePassword(auth);
- }
+ HashedPassword hashedPassword = initializeEncryptionMethod(algorithm, playerName)
+ .computeHash(password, playerName);
+ dataSource.updatePassword(playerName, hashedPassword);
}
}
diff --git a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java
index f18a05707..3e963ed92 100644
--- a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java
+++ b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java
@@ -10,6 +10,7 @@ import fr.xephi.authme.security.crypts.PHPBB;
import org.bukkit.event.Event;
import org.bukkit.plugin.PluginManager;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
@@ -126,6 +127,7 @@ public class PasswordSecurityTest {
verify(method, never()).comparePassword(anyString(), any(HashedPassword.class), anyString());
}
+ @Ignore
@Test
public void shouldTryOtherMethodsForFailedPassword() {
// given
From d46a2f0755973e8cd1d3083172f9bb4b624ce6c6 Mon Sep 17 00:00:00 2001
From: ljacqu
Date: Thu, 31 Dec 2015 13:52:16 +0100
Subject: [PATCH 5/9] Fix test in PasswordSecurity for new savePassword()
---
.../authme/security/PasswordSecurityTest.java | 15 +++------------
1 file changed, 3 insertions(+), 12 deletions(-)
diff --git a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java
index 3e963ed92..d36810ab6 100644
--- a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java
+++ b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java
@@ -127,7 +127,6 @@ public class PasswordSecurityTest {
verify(method, never()).comparePassword(anyString(), any(HashedPassword.class), anyString());
}
- @Ignore
@Test
public void shouldTryOtherMethodsForFailedPassword() {
// given
@@ -140,11 +139,7 @@ public class PasswordSecurityTest {
// MD5 hash for "Test"
HashedPassword newPassword = new HashedPassword("0cbc6611f5540bd0809a388dc95a615b");
- PlayerAuth auth = mock(PlayerAuth.class);
- doCallRealMethod().when(auth).getPassword();
- doCallRealMethod().when(auth).setPassword(any(HashedPassword.class));
- auth.setPassword(password);
- given(dataSource.getAuth(argThat(equalToIgnoringCase(playerName)))).willReturn(auth);
+ given(dataSource.getPassword(argThat(equalToIgnoringCase(playerName)))).willReturn(password);
given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(false);
given(method.computeHash(clearTextPass, playerLowerCase)).willReturn(newPassword);
PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.MD5, pluginManager, true);
@@ -157,14 +152,10 @@ public class PasswordSecurityTest {
// Note ljacqu 20151230: We need to check the player name in a case-insensitive way because the methods within
// PasswordSecurity may convert the name into all lower-case. This is desired because EncryptionMethod methods
// should only be invoked with all lower-case names. Data source is case-insensitive itself, so this is fine.
- verify(dataSource, times(2)).getAuth(argThat(equalToIgnoringCase(playerName)));
+ verify(dataSource).getPassword(argThat(equalToIgnoringCase(playerName)));
verify(pluginManager, times(2)).callEvent(any(PasswordEncryptionEvent.class));
verify(method).comparePassword(clearTextPass, password, playerLowerCase);
- verify(auth).setPassword(newPassword);
-
- ArgumentCaptor captor = ArgumentCaptor.forClass(PlayerAuth.class);
- verify(dataSource).updatePassword(captor.capture());
- assertThat(captor.getValue().getPassword(), equalTo(newPassword));
+ verify(dataSource).updatePassword(playerLowerCase, newPassword);
}
@Test
From da5de58afb1b503cb77cac574234aafad4d1fa71 Mon Sep 17 00:00:00 2001
From: DNx5
Date: Thu, 7 Jan 2016 06:15:39 +0700
Subject: [PATCH 6/9] Rename XF class into XFBCRYPT.
---
.../fr/xephi/authme/datasource/MySQL.java | 20 +++++++++----------
.../xephi/authme/security/HashAlgorithm.java | 2 +-
.../crypts/{XF.java => XFBCRYPT.java} | 2 +-
.../fr/xephi/authme/settings/Settings.java | 10 +---------
.../HashAlgorithmIntegrationTest.java | 4 ++--
.../crypts/{XFTest.java => XFBCRYPTTest.java} | 10 +++++-----
6 files changed, 20 insertions(+), 28 deletions(-)
rename src/main/java/fr/xephi/authme/security/crypts/{XF.java => XFBCRYPT.java} (93%)
rename src/test/java/fr/xephi/authme/security/crypts/{XFTest.java => XFBCRYPTTest.java} (58%)
diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java
index ebaf5d8d6..0e17ea566 100644
--- a/src/main/java/fr/xephi/authme/datasource/MySQL.java
+++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java
@@ -7,7 +7,7 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.security.crypts.HashedPassword;
-import fr.xephi.authme.security.crypts.XF;
+import fr.xephi.authme.security.crypts.XFBCRYPT;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.StringUtils;
@@ -300,14 +300,14 @@ public class MySQL implements DataSource {
.build();
rs.close();
pst.close();
- if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
+ if (Settings.getPasswordHash == HashAlgorithm.XFBCRYPT) {
pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + columnID + "=?;");
pst.setInt(1, id);
rs = pst.executeQuery();
if (rs.next()) {
Blob blob = rs.getBlob("data");
byte[] bytes = blob.getBytes(1, (int) blob.length());
- pAuth.setPassword(new HashedPassword(XF.getHashFromBlob(bytes)));
+ pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes)));
}
}
} catch (SQLException ex) {
@@ -490,7 +490,7 @@ public class MySQL implements DataSource {
}
rs.close();
pst.close();
- } else if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
+ } else if (Settings.getPasswordHash == HashAlgorithm.XFBCRYPT) {
pst = con.prepareStatement("SELECT " + columnID + " FROM " + tableName + " WHERE " + columnName + "=?;");
pst.setString(1, auth.getNickname());
rs = pst.executeQuery();
@@ -544,7 +544,7 @@ public class MySQL implements DataSource {
}
pst.executeUpdate();
pst.close();
- if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
+ if (Settings.getPasswordHash == HashAlgorithm.XFBCRYPT) {
String sql = "SELECT " + columnID + " FROM " + tableName + " WHERE " + columnName + "=?;";
pst = con.prepareStatement(sql);
pst.setString(1, user);
@@ -641,7 +641,7 @@ public class MySQL implements DataSource {
try (Connection con = getConnection()) {
String sql;
PreparedStatement pst;
- if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
+ if (Settings.getPasswordHash == HashAlgorithm.XFBCRYPT) {
sql = "SELECT " + columnID + " FROM " + tableName + " WHERE " + columnName + "=?;";
pst = con.prepareStatement(sql);
pst.setString(1, user);
@@ -942,14 +942,14 @@ public class MySQL implements DataSource {
.groupId(group)
.build();
- if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
+ if (Settings.getPasswordHash == HashAlgorithm.XFBCRYPT) {
int id = rs.getInt(columnID);
pst.setInt(1, id);
ResultSet rs2 = pst.executeQuery();
if (rs2.next()) {
Blob blob = rs2.getBlob("data");
byte[] bytes = blob.getBytes(1, (int) blob.length());
- pAuth.setPassword(new HashedPassword(XF.getHashFromBlob(bytes)));
+ pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes)));
}
rs2.close();
}
@@ -989,14 +989,14 @@ public class MySQL implements DataSource {
.groupId(group)
.build();
- if (Settings.getPasswordHash == HashAlgorithm.XENFORO) {
+ if (Settings.getPasswordHash == HashAlgorithm.XFBCRYPT) {
int id = rs.getInt(columnID);
pst.setInt(1, id);
ResultSet rs2 = pst.executeQuery();
if (rs2.next()) {
Blob blob = rs2.getBlob("data");
byte[] bytes = blob.getBytes(1, (int) blob.length());
- pAuth.setPassword(new HashedPassword(XF.getHashFromBlob(bytes)));
+ pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes)));
}
rs2.close();
}
diff --git a/src/main/java/fr/xephi/authme/security/HashAlgorithm.java b/src/main/java/fr/xephi/authme/security/HashAlgorithm.java
index 6d9ab98dc..389ad485f 100644
--- a/src/main/java/fr/xephi/authme/security/HashAlgorithm.java
+++ b/src/main/java/fr/xephi/authme/security/HashAlgorithm.java
@@ -35,7 +35,7 @@ public enum HashAlgorithm {
WHIRLPOOL(fr.xephi.authme.security.crypts.WHIRLPOOL.class),
WORDPRESS(fr.xephi.authme.security.crypts.WORDPRESS.class),
XAUTH(fr.xephi.authme.security.crypts.XAUTH.class),
- XENFORO(fr.xephi.authme.security.crypts.XF.class),
+ XFBCRYPT(fr.xephi.authme.security.crypts.XFBCRYPT.class),
CUSTOM(null);
private final Class extends EncryptionMethod> clazz;
diff --git a/src/main/java/fr/xephi/authme/security/crypts/XF.java b/src/main/java/fr/xephi/authme/security/crypts/XFBCRYPT.java
similarity index 93%
rename from src/main/java/fr/xephi/authme/security/crypts/XF.java
rename to src/main/java/fr/xephi/authme/security/crypts/XFBCRYPT.java
index cd3bcf165..6666a076c 100644
--- a/src/main/java/fr/xephi/authme/security/crypts/XF.java
+++ b/src/main/java/fr/xephi/authme/security/crypts/XFBCRYPT.java
@@ -3,7 +3,7 @@ package fr.xephi.authme.security.crypts;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-public class XF extends BCRYPT {
+public class XFBCRYPT extends BCRYPT {
private static final Pattern HASH_PATTERN = Pattern.compile("\"hash\";s.*\"(.*)?\"");
@Override
diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/src/main/java/fr/xephi/authme/settings/Settings.java
index cfb28f75e..4adfe15bd 100644
--- a/src/main/java/fr/xephi/authme/settings/Settings.java
+++ b/src/main/java/fr/xephi/authme/settings/Settings.java
@@ -515,11 +515,6 @@ public final class Settings {
set("Xenoforo.predefinedSalt", null);
changes = true;
}
- if (configFile.getString("settings.security.passwordHash", "SHA256").toUpperCase().equals("XFSHA1") ||
- configFile.getString("settings.security.passwordHash", "SHA256").toUpperCase().equals("XFSHA256")) {
- set("settings.security.passwordHash", "XENFORO");
- changes = true;
- }
if (!contains("Protection.enableProtection")) {
set("Protection.enableProtection", false);
changes = true;
@@ -528,10 +523,6 @@ public final class Settings {
set("settings.restrictions.removeSpeed", true);
changes = true;
}
- if (!contains("DataSource.mySQLMaxConections")) {
- set("DataSource.mySQLMaxConections", 25);
- changes = true;
- }
if (!contains("Protection.countries")) {
countries = new ArrayList<>();
countries.add("US");
@@ -741,6 +732,7 @@ public final class Settings {
}
if (changes) {
+ save();
plugin.getLogger().warning("Merged new Config Options - I'm not an error, please don't report me");
plugin.getLogger().warning("Please check your config.yml file for new configs!");
}
diff --git a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java
index 4583940e2..0ecfdb20b 100644
--- a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java
+++ b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java
@@ -48,8 +48,8 @@ public class HashAlgorithmIntegrationTest {
// given / when / then
for (HashAlgorithm algorithm : HashAlgorithm.values()) {
// TODO #137: Remove this check
- if (HashAlgorithm.XENFORO.equals(algorithm)) {
- System.out.println("Note: Currently skipping XENFORO hash (TODO #137: Fix it)");
+ if (HashAlgorithm.XFBCRYPT.equals(algorithm)) {
+ System.out.println("Note: Currently skipping XFBCRYPT hash (TODO #137: Fix it)");
continue;
}
diff --git a/src/test/java/fr/xephi/authme/security/crypts/XFTest.java b/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java
similarity index 58%
rename from src/test/java/fr/xephi/authme/security/crypts/XFTest.java
rename to src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java
index d9226baca..b62fb01e9 100644
--- a/src/test/java/fr/xephi/authme/security/crypts/XFTest.java
+++ b/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java
@@ -4,16 +4,16 @@ import org.junit.Ignore;
import org.junit.Test;
/**
- * Test for {@link XF}.
+ * Test for {@link XFBCRYPT}.
*/
@Ignore
// TODO #137: Create a test class as for the other encryption methods. Simply run the following test and copy the
-// output -- that's your test class! (Once XF.java actually works properly)
-// @org.junit.Test public void a() { AbstractEncryptionMethodTest.generateTest(new XF()); }
-public class XFTest {
+// output -- that's your test class! (Once XFBCRYPT.java actually works properly)
+// @org.junit.Test public void a() { AbstractEncryptionMethodTest.generateTest(new XFBCRYPT()); }
+public class XFBCRYPTTest {
@Test
public void shouldComputeHash() {
- System.out.println(new XF().computeHash("Test", "name"));
+ System.out.println(new XFBCRYPT().computeHash("Test", "name"));
}
}
From 2c13783c6cbe298a2dd6e6c998b51cd9b6980064 Mon Sep 17 00:00:00 2001
From: DNx5
Date: Thu, 7 Jan 2016 07:12:30 +0700
Subject: [PATCH 7/9] Created test for XFBCRYPT.
---
.../authme/security/crypts/XFBCRYPTTest.java | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java b/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java
index b62fb01e9..3116d1a3e 100644
--- a/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java
+++ b/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java
@@ -1,19 +1,17 @@
package fr.xephi.authme.security.crypts;
-import org.junit.Ignore;
import org.junit.Test;
/**
* Test for {@link XFBCRYPT}.
*/
-@Ignore
-// TODO #137: Create a test class as for the other encryption methods. Simply run the following test and copy the
-// output -- that's your test class! (Once XFBCRYPT.java actually works properly)
-// @org.junit.Test public void a() { AbstractEncryptionMethodTest.generateTest(new XFBCRYPT()); }
-public class XFBCRYPTTest {
+public class XFBCRYPTTest extends AbstractEncryptionMethodTest {
- @Test
- public void shouldComputeHash() {
- System.out.println(new XFBCRYPT().computeHash("Test", "name"));
+ public XFBCRYPTTest() {
+ super(new XFBCRYPT(),
+ "$2a$10$UtuON/ZG.x8EWG/zQbryB.BHfQVrfxk3H7qykzP.UJQ8YiLjZyfqq", // password
+ "$2a$10$Q.ocUo.YtHTdI4nu3pcpKun6BILcmWHm541ANULucmuU/ps1QKY4K", // PassWord1
+ "$2a$10$yHjm02.K4HP5iFU1F..yLeTeo7PWZVbKAr/QGex5jU4.J3mdq/uuO", // &^%te$t?Pw@_
+ "$2a$10$joIayhGStExKWxNbiqMMPOYFSpQ76HVNjpOB7.QwTmG5q.TiJJ.0e"); // âË_3(íù*
}
}
From fab13c586f87d059765160236ba7bcfa3486213d Mon Sep 17 00:00:00 2001
From: DNx5
Date: Thu, 7 Jan 2016 07:41:09 +0700
Subject: [PATCH 8/9] Remove skip test for Xenforo hash.
---
.../xephi/authme/security/HashAlgorithmIntegrationTest.java | 6 ------
.../java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java | 2 --
2 files changed, 8 deletions(-)
diff --git a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java
index 0ecfdb20b..2472c6592 100644
--- a/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java
+++ b/src/test/java/fr/xephi/authme/security/HashAlgorithmIntegrationTest.java
@@ -47,12 +47,6 @@ public class HashAlgorithmIntegrationTest {
public void shouldBeAbleToInstantiateEncryptionAlgorithms() throws InstantiationException, IllegalAccessException {
// given / when / then
for (HashAlgorithm algorithm : HashAlgorithm.values()) {
- // TODO #137: Remove this check
- if (HashAlgorithm.XFBCRYPT.equals(algorithm)) {
- System.out.println("Note: Currently skipping XFBCRYPT hash (TODO #137: Fix it)");
- continue;
- }
-
if (!HashAlgorithm.CUSTOM.equals(algorithm)) {
EncryptionMethod method = algorithm.getClazz().newInstance();
HashedPassword hashedPassword = method.computeHash("pwd", "name");
diff --git a/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java b/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java
index 3116d1a3e..4edafbdd0 100644
--- a/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java
+++ b/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java
@@ -1,7 +1,5 @@
package fr.xephi.authme.security.crypts;
-import org.junit.Test;
-
/**
* Test for {@link XFBCRYPT}.
*/
From 120e69ac67f55a16d7fb91537529b3379c7987ab Mon Sep 17 00:00:00 2001
From: ljacqu
Date: Thu, 7 Jan 2016 20:49:41 +0100
Subject: [PATCH 9/9] Fix null pointer in tests
---
.../java/fr/xephi/authme/security/crypts/BcryptTest.java | 6 +++---
.../fr/xephi/authme/security/crypts/XFBCRYPTTest.java | 8 ++++++++
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/src/test/java/fr/xephi/authme/security/crypts/BcryptTest.java b/src/test/java/fr/xephi/authme/security/crypts/BcryptTest.java
index 65ea3b68c..106689ae7 100644
--- a/src/test/java/fr/xephi/authme/security/crypts/BcryptTest.java
+++ b/src/test/java/fr/xephi/authme/security/crypts/BcryptTest.java
@@ -2,15 +2,15 @@ package fr.xephi.authme.security.crypts;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.WrapperMock;
-import org.junit.Before;
+import org.junit.BeforeClass;
/**
* Test for {@link BCRYPT}.
*/
public class BcryptTest extends AbstractEncryptionMethodTest {
- @Before
- public void setUpSettings() {
+ @BeforeClass
+ public static void setUpSettings() {
WrapperMock.createInstance();
Settings.bCryptLog2Rounds = 8;
}
diff --git a/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java b/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java
index 4edafbdd0..3b7294a04 100644
--- a/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java
+++ b/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java
@@ -1,10 +1,18 @@
package fr.xephi.authme.security.crypts;
+import fr.xephi.authme.util.WrapperMock;
+import org.junit.BeforeClass;
+
/**
* Test for {@link XFBCRYPT}.
*/
public class XFBCRYPTTest extends AbstractEncryptionMethodTest {
+ @BeforeClass
+ public static void setUpWrapper() {
+ WrapperMock.createInstance();
+ }
+
public XFBCRYPTTest() {
super(new XFBCRYPT(),
"$2a$10$UtuON/ZG.x8EWG/zQbryB.BHfQVrfxk3H7qykzP.UJQ8YiLjZyfqq", // password