Minor - rename EncryptedPassword to HashedPassword
- We hash passwords; we don't encrypt them
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
package fr.xephi.authme.security;
|
||||
|
||||
import fr.xephi.authme.security.crypts.EncryptedPassword;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.security.crypts.EncryptionMethod;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.util.StringUtils;
|
||||
@@ -49,11 +49,11 @@ public class HashAlgorithmIntegrationTest {
|
||||
for (HashAlgorithm algorithm : HashAlgorithm.values()) {
|
||||
if (!HashAlgorithm.CUSTOM.equals(algorithm)) {
|
||||
EncryptionMethod method = algorithm.getClazz().newInstance();
|
||||
EncryptedPassword encryptedPassword = method.computeHash("pwd", "name");
|
||||
HashedPassword hashedPassword = method.computeHash("pwd", "name");
|
||||
assertThat("Salt should not be null if method.hasSeparateSalt(), and vice versa. Method: '"
|
||||
+ method + "'", StringUtils.isEmpty(encryptedPassword.getSalt()), equalTo(!method.hasSeparateSalt()));
|
||||
+ method + "'", StringUtils.isEmpty(hashedPassword.getSalt()), equalTo(!method.hasSeparateSalt()));
|
||||
assertThat("Hash should not be empty for method '" + method + "'",
|
||||
StringUtils.isEmpty(encryptedPassword.getHash()), equalTo(false));
|
||||
StringUtils.isEmpty(hashedPassword.getHash()), equalTo(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ 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.EncryptedPassword;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import fr.xephi.authme.security.crypts.EncryptionMethod;
|
||||
import fr.xephi.authme.security.crypts.JOOMLA;
|
||||
import fr.xephi.authme.security.crypts.PHPBB;
|
||||
@@ -64,7 +64,7 @@ public class PasswordSecurityTest {
|
||||
@Test
|
||||
public void shouldReturnPasswordMatch() {
|
||||
// given
|
||||
EncryptedPassword password = new EncryptedPassword("$TEST$10$SOME_HASH", null);
|
||||
HashedPassword password = new HashedPassword("$TEST$10$SOME_HASH", null);
|
||||
String playerName = "Tester";
|
||||
// Calls to EncryptionMethod are always with the lower-case version of the name
|
||||
String playerLowerCase = playerName.toLowerCase();
|
||||
@@ -89,7 +89,7 @@ public class PasswordSecurityTest {
|
||||
@Test
|
||||
public void shouldReturnPasswordMismatch() {
|
||||
// given
|
||||
EncryptedPassword password = new EncryptedPassword("$TEST$10$SOME_HASH", null);
|
||||
HashedPassword password = new HashedPassword("$TEST$10$SOME_HASH", null);
|
||||
String playerName = "My_PLayer";
|
||||
String playerLowerCase = playerName.toLowerCase();
|
||||
String clearTextPass = "passw0Rd1";
|
||||
@@ -126,24 +126,24 @@ public class PasswordSecurityTest {
|
||||
assertThat(result, equalTo(false));
|
||||
verify(dataSource).getAuth(playerName);
|
||||
verify(pluginManager, never()).callEvent(any(Event.class));
|
||||
verify(method, never()).comparePassword(anyString(), any(EncryptedPassword.class), anyString());
|
||||
verify(method, never()).comparePassword(anyString(), any(HashedPassword.class), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldTryOtherMethodsForFailedPassword() {
|
||||
// given
|
||||
// BCRYPT hash for "Test"
|
||||
EncryptedPassword password =
|
||||
new EncryptedPassword("$2y$10$2e6d2193f43501c926e25elvWlPmWczmrfrnbZV0dUZGITjYjnkkW");
|
||||
HashedPassword password =
|
||||
new HashedPassword("$2y$10$2e6d2193f43501c926e25elvWlPmWczmrfrnbZV0dUZGITjYjnkkW");
|
||||
String playerName = "somePlayer";
|
||||
String playerLowerCase = playerName.toLowerCase();
|
||||
String clearTextPass = "Test";
|
||||
// MD5 hash for "Test"
|
||||
EncryptedPassword newPassword = new EncryptedPassword("0cbc6611f5540bd0809a388dc95a615b");
|
||||
HashedPassword newPassword = new HashedPassword("0cbc6611f5540bd0809a388dc95a615b");
|
||||
|
||||
PlayerAuth auth = mock(PlayerAuth.class);
|
||||
doCallRealMethod().when(auth).getPassword();
|
||||
doCallRealMethod().when(auth).setPassword(any(EncryptedPassword.class));
|
||||
doCallRealMethod().when(auth).setPassword(any(HashedPassword.class));
|
||||
auth.setPassword(password);
|
||||
given(dataSource.getAuth(argThat(equalToIgnoringCase(playerName)))).willReturn(auth);
|
||||
given(method.comparePassword(clearTextPass, password, playerLowerCase)).willReturn(false);
|
||||
@@ -174,15 +174,15 @@ public class PasswordSecurityTest {
|
||||
String password = "MyP@ssword";
|
||||
String username = "theUserInTest";
|
||||
String usernameLowerCase = username.toLowerCase();
|
||||
EncryptedPassword encryptedPassword = new EncryptedPassword("$T$est#Hash", "__someSalt__");
|
||||
given(method.computeHash(password, usernameLowerCase)).willReturn(encryptedPassword);
|
||||
HashedPassword hashedPassword = new HashedPassword("$T$est#Hash", "__someSalt__");
|
||||
given(method.computeHash(password, usernameLowerCase)).willReturn(hashedPassword);
|
||||
PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.JOOMLA, pluginManager, true);
|
||||
|
||||
// when
|
||||
EncryptedPassword result = security.computeHash(password, username);
|
||||
HashedPassword result = security.computeHash(password, username);
|
||||
|
||||
// then
|
||||
assertThat(result, equalTo(encryptedPassword));
|
||||
assertThat(result, equalTo(hashedPassword));
|
||||
ArgumentCaptor<PasswordEncryptionEvent> captor = ArgumentCaptor.forClass(PasswordEncryptionEvent.class);
|
||||
verify(pluginManager).callEvent(captor.capture());
|
||||
PasswordEncryptionEvent event = captor.getValue();
|
||||
@@ -195,15 +195,15 @@ public class PasswordSecurityTest {
|
||||
// given
|
||||
String password = "TopSecretPass#112525";
|
||||
String username = "someone12";
|
||||
EncryptedPassword encryptedPassword = new EncryptedPassword("~T!est#Hash", "__someSalt__");
|
||||
given(method.computeHash(password, username)).willReturn(encryptedPassword);
|
||||
HashedPassword hashedPassword = new HashedPassword("~T!est#Hash", "__someSalt__");
|
||||
given(method.computeHash(password, username)).willReturn(hashedPassword);
|
||||
PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.JOOMLA, pluginManager, true);
|
||||
|
||||
// when
|
||||
EncryptedPassword result = security.computeHash(HashAlgorithm.PHPBB, password, username);
|
||||
HashedPassword result = security.computeHash(HashAlgorithm.PHPBB, password, username);
|
||||
|
||||
// then
|
||||
assertThat(result, equalTo(encryptedPassword));
|
||||
assertThat(result, equalTo(hashedPassword));
|
||||
ArgumentCaptor<PasswordEncryptionEvent> captor = ArgumentCaptor.forClass(PasswordEncryptionEvent.class);
|
||||
verify(pluginManager).callEvent(captor.capture());
|
||||
PasswordEncryptionEvent event = captor.getValue();
|
||||
@@ -216,19 +216,19 @@ public class PasswordSecurityTest {
|
||||
// given
|
||||
String password = "?topSecretPass\\";
|
||||
String username = "someone12";
|
||||
EncryptedPassword encryptedPassword = new EncryptedPassword("~T!est#Hash");
|
||||
given(method.computeHash(password, username)).willReturn(encryptedPassword);
|
||||
HashedPassword hashedPassword = new HashedPassword("~T!est#Hash");
|
||||
given(method.computeHash(password, username)).willReturn(hashedPassword);
|
||||
given(method.hasSeparateSalt()).willReturn(true);
|
||||
PasswordSecurity security = new PasswordSecurity(dataSource, HashAlgorithm.XAUTH, pluginManager, true);
|
||||
|
||||
// when
|
||||
boolean result = security.comparePassword(password, encryptedPassword, username);
|
||||
boolean result = security.comparePassword(password, hashedPassword, username);
|
||||
|
||||
// then
|
||||
assertThat(result, equalTo(false));
|
||||
verify(dataSource, never()).getAuth(anyString());
|
||||
verify(pluginManager).callEvent(any(PasswordEncryptionEvent.class));
|
||||
verify(method, never()).comparePassword(anyString(), any(EncryptedPassword.class), anyString());
|
||||
verify(method, never()).comparePassword(anyString(), any(HashedPassword.class), anyString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public abstract class AbstractEncryptionMethodTest {
|
||||
/** The encryption method to test. */
|
||||
private EncryptionMethod method;
|
||||
/** Map with the hashes against which the entries in GIVEN_PASSWORDS are tested. */
|
||||
private Map<String, EncryptedPassword> hashes;
|
||||
private Map<String, HashedPassword> hashes;
|
||||
|
||||
/**
|
||||
* Create a new test for the given encryption method.
|
||||
@@ -45,7 +45,7 @@ public abstract class AbstractEncryptionMethodTest {
|
||||
*/
|
||||
public AbstractEncryptionMethodTest(EncryptionMethod method, String... computedHashes) {
|
||||
if (method.hasSeparateSalt()) {
|
||||
throw new UnsupportedOperationException("Test must be initialized with EncryptedPassword objects if "
|
||||
throw new UnsupportedOperationException("Test must be initialized with HashedPassword objects if "
|
||||
+ "the salt is stored separately. Use the other constructor");
|
||||
} else if (computedHashes.length != GIVEN_PASSWORDS.length) {
|
||||
throw new UnsupportedOperationException("Expected " + GIVEN_PASSWORDS.length + " hashes");
|
||||
@@ -54,12 +54,12 @@ public abstract class AbstractEncryptionMethodTest {
|
||||
|
||||
hashes = new HashMap<>();
|
||||
for (int i = 0; i < GIVEN_PASSWORDS.length; ++i) {
|
||||
hashes.put(GIVEN_PASSWORDS[i], new EncryptedPassword(computedHashes[i]));
|
||||
hashes.put(GIVEN_PASSWORDS[i], new HashedPassword(computedHashes[i]));
|
||||
}
|
||||
}
|
||||
|
||||
public AbstractEncryptionMethodTest(EncryptionMethod method, EncryptedPassword result0, EncryptedPassword result1,
|
||||
EncryptedPassword result2, EncryptedPassword result3) {
|
||||
public AbstractEncryptionMethodTest(EncryptionMethod method, HashedPassword result0, HashedPassword result1,
|
||||
HashedPassword result2, HashedPassword result3) {
|
||||
if (!method.hasSeparateSalt()) {
|
||||
throw new UnsupportedOperationException("Salt is not stored separately, so test should be initialized"
|
||||
+ " with the password hashes only. Use the other constructor");
|
||||
@@ -102,7 +102,7 @@ public abstract class AbstractEncryptionMethodTest {
|
||||
for (String password : internalPasswords) {
|
||||
final String salt = method.generateSalt();
|
||||
final String hash = method.computeHash(password, salt, USERNAME);
|
||||
EncryptedPassword encryptedPassword = new EncryptedPassword(hash, salt);
|
||||
HashedPassword hashedPassword = new HashedPassword(hash, salt);
|
||||
|
||||
// Check that the computeHash(password, salt, name) method has the same output for the returned salt
|
||||
if (testHashEqualityForSameSalt()) {
|
||||
@@ -111,14 +111,14 @@ public abstract class AbstractEncryptionMethodTest {
|
||||
}
|
||||
|
||||
assertTrue("Generated hash for '" + password + "' should match password (hash = '" + hash + "')",
|
||||
method.comparePassword(password, encryptedPassword, USERNAME));
|
||||
method.comparePassword(password, hashedPassword, USERNAME));
|
||||
if (!password.equals(password.toLowerCase())) {
|
||||
assertFalse("Lower-case of '" + password + "' should not match generated hash '" + hash + "'",
|
||||
method.comparePassword(password.toLowerCase(), encryptedPassword, USERNAME));
|
||||
method.comparePassword(password.toLowerCase(), hashedPassword, USERNAME));
|
||||
}
|
||||
if (!password.equals(password.toUpperCase())) {
|
||||
assertFalse("Upper-case of '" + password + "' should not match generated hash '" + hash + "'",
|
||||
method.comparePassword(password.toUpperCase(), encryptedPassword, USERNAME));
|
||||
method.comparePassword(password.toUpperCase(), hashedPassword, USERNAME));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,9 +143,9 @@ public abstract class AbstractEncryptionMethodTest {
|
||||
}
|
||||
|
||||
if (method.hasSeparateSalt()) {
|
||||
EncryptedPassword encryptedPassword = method.computeHash(password, USERNAME);
|
||||
System.out.println(String.format("\t\tnew EncryptedPassword(\"%s\", \"%s\")%s// %s",
|
||||
encryptedPassword.getHash(), encryptedPassword.getSalt(), delim, password));
|
||||
HashedPassword hashedPassword = method.computeHash(password, USERNAME);
|
||||
System.out.println(String.format("\t\tnew HashedPassword(\"%s\", \"%s\")%s// %s",
|
||||
hashedPassword.getHash(), hashedPassword.getSalt(), delim, password));
|
||||
} else {
|
||||
System.out.println("\t\t\"" + method.computeHash(password, USERNAME).getHash()
|
||||
+ "\"" + delim + "// " + password);
|
||||
|
||||
@@ -7,10 +7,10 @@ public class IPB3Test extends AbstractEncryptionMethodTest {
|
||||
|
||||
public IPB3Test() {
|
||||
super(new IPB3(),
|
||||
new EncryptedPassword("f8ecea1ce42b5babef369ff7692dbe3f", "1715b"), //password
|
||||
new EncryptedPassword("40a93731a931352e0619cdf09b975040", "ba91c"), //PassWord1
|
||||
new EncryptedPassword("a77ca982373946d5800430bd2947ba11", "a7725"), //&^%te$t?Pw@_
|
||||
new EncryptedPassword("383d7b9e2b707d6e894ec7b30e3032c3", "fa9fd")); //âË_3(íù*
|
||||
new HashedPassword("f8ecea1ce42b5babef369ff7692dbe3f", "1715b"), //password
|
||||
new HashedPassword("40a93731a931352e0619cdf09b975040", "ba91c"), //PassWord1
|
||||
new HashedPassword("a77ca982373946d5800430bd2947ba11", "a7725"), //&^%te$t?Pw@_
|
||||
new HashedPassword("383d7b9e2b707d6e894ec7b30e3032c3", "fa9fd")); //âË_3(íù*
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ public class MYBBTest extends AbstractEncryptionMethodTest {
|
||||
|
||||
public MYBBTest() {
|
||||
super(new MYBB(),
|
||||
new EncryptedPassword("57c7a16d860833db5030738f5a465d2b", "acdc14e6"), //password
|
||||
new EncryptedPassword("08fbdf721f2c42d9780b7d66df0ba830", "792fd7fb"), //PassWord1
|
||||
new EncryptedPassword("d602f38fb59ad9e185d5604f5d4ddb36", "4b5534a4"), //&^%te$t?Pw@_
|
||||
new EncryptedPassword("b3c39410d0ab8ae2a65c257820797fad", "e5a6cb14")); //âË_3(íù*
|
||||
new HashedPassword("57c7a16d860833db5030738f5a465d2b", "acdc14e6"), //password
|
||||
new HashedPassword("08fbdf721f2c42d9780b7d66df0ba830", "792fd7fb"), //PassWord1
|
||||
new HashedPassword("d602f38fb59ad9e185d5604f5d4ddb36", "4b5534a4"), //&^%te$t?Pw@_
|
||||
new HashedPassword("b3c39410d0ab8ae2a65c257820797fad", "e5a6cb14")); //âË_3(íù*
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ public class PHPFUSIONTest extends AbstractEncryptionMethodTest {
|
||||
|
||||
public PHPFUSIONTest() {
|
||||
super(new PHPFUSION(),
|
||||
new EncryptedPassword("f7a606c4eb3fcfbc382906476e05b06f21234a77d1a4eacc0f93f503deb69e70", "6cd1c97c55cb"), // password
|
||||
new EncryptedPassword("8a9b7bb706a3347e5f684a7cb905bfb26b9a0d099358064139ab3ed1a66aeb2b", "d6012370b73f"), // PassWord1
|
||||
new EncryptedPassword("43f2f23f44c8f89e2dbf06050bc8c77dbcdf71a7b5d28c87ec657d474e63d62d", "f75400a209a4"), // &^%te$t?Pw@_
|
||||
new EncryptedPassword("4e7f4eb7e3653d7460f1cf590def4153c6fcdf8b8e16fb95538fdf9e54a95245", "d552e0f5b23a")); // âË_3(íù*
|
||||
new HashedPassword("f7a606c4eb3fcfbc382906476e05b06f21234a77d1a4eacc0f93f503deb69e70", "6cd1c97c55cb"), // password
|
||||
new HashedPassword("8a9b7bb706a3347e5f684a7cb905bfb26b9a0d099358064139ab3ed1a66aeb2b", "d6012370b73f"), // PassWord1
|
||||
new HashedPassword("43f2f23f44c8f89e2dbf06050bc8c77dbcdf71a7b5d28c87ec657d474e63d62d", "f75400a209a4"), // &^%te$t?Pw@_
|
||||
new HashedPassword("4e7f4eb7e3653d7460f1cf590def4153c6fcdf8b8e16fb95538fdf9e54a95245", "d552e0f5b23a")); // âË_3(íù*
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ public class SALTED2MD5Test extends AbstractEncryptionMethodTest {
|
||||
|
||||
public SALTED2MD5Test() {
|
||||
super(new SALTED2MD5(),
|
||||
new EncryptedPassword("9f3d13dc01a6fe61fd669954174399f3", "9b5f5749"), // password
|
||||
new EncryptedPassword("b28c32f624a4eb161d6adc9acb5bfc5b", "f750ba32"), // PassWord1
|
||||
new EncryptedPassword("38dcb83cc68424afe3cda012700c2bb1", "eb2c3394"), // &^%te$t?Pw@_
|
||||
new EncryptedPassword("ad25606eae5b760c8a2469d65578ac39", "04eee598")); // âË_3(íù*)
|
||||
new HashedPassword("9f3d13dc01a6fe61fd669954174399f3", "9b5f5749"), // password
|
||||
new HashedPassword("b28c32f624a4eb161d6adc9acb5bfc5b", "f750ba32"), // PassWord1
|
||||
new HashedPassword("38dcb83cc68424afe3cda012700c2bb1", "eb2c3394"), // &^%te$t?Pw@_
|
||||
new HashedPassword("ad25606eae5b760c8a2469d65578ac39", "04eee598")); // âË_3(íù*)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ public class SALTEDSHA512Test extends AbstractEncryptionMethodTest {
|
||||
|
||||
public SALTEDSHA512Test() {
|
||||
super(new SALTEDSHA512(),
|
||||
new EncryptedPassword("dea7a37cecf5384ae8e347fd1411efb51364b6ba1b328695de3b354612c1d7010807e8b7051c40f740e498490e1f133e2c2408327d13fbdd68e1b1f6d548e624", "29f8a3c52147f987fee7ba3e0fb311bd"), // password
|
||||
new EncryptedPassword("7c06225aac574d2dc7c81a2ed306637adf025715f52083e05bdab014faaa234e24a97d0e69ea0108dfa77cc9228e58be319ee677e679b5d1ad168d40e50a42f6", "8ea37b85d020b98f60c0fe9b8ec9296c"), // PassWord1
|
||||
new EncryptedPassword("55711adbe03c9616f3505f0d57077fdd528c32243eb6f9840c1a6ff9e553940d6b89790750ebd52ebda63ca793fbe9980d54057af40836820c648750fe22d49c", "9f58079631ef21d32b4710694f1f461b"), // &^%te$t?Pw@_
|
||||
new EncryptedPassword("29dc5be8702975ea4563ed3de5b145e2d2f1c37ae661bbe0d3e94d964402cf09d539d65f3b90ff6921ea3d40727f76fb38fb34d1e5c2d62238c4e0203efc372f", "048bb76168265d906f1fd1f81d0616a9")); // âË_3(íù*
|
||||
new HashedPassword("dea7a37cecf5384ae8e347fd1411efb51364b6ba1b328695de3b354612c1d7010807e8b7051c40f740e498490e1f133e2c2408327d13fbdd68e1b1f6d548e624", "29f8a3c52147f987fee7ba3e0fb311bd"), // password
|
||||
new HashedPassword("7c06225aac574d2dc7c81a2ed306637adf025715f52083e05bdab014faaa234e24a97d0e69ea0108dfa77cc9228e58be319ee677e679b5d1ad168d40e50a42f6", "8ea37b85d020b98f60c0fe9b8ec9296c"), // PassWord1
|
||||
new HashedPassword("55711adbe03c9616f3505f0d57077fdd528c32243eb6f9840c1a6ff9e553940d6b89790750ebd52ebda63ca793fbe9980d54057af40836820c648750fe22d49c", "9f58079631ef21d32b4710694f1f461b"), // &^%te$t?Pw@_
|
||||
new HashedPassword("29dc5be8702975ea4563ed3de5b145e2d2f1c37ae661bbe0d3e94d964402cf09d539d65f3b90ff6921ea3d40727f76fb38fb34d1e5c2d62238c4e0203efc372f", "048bb76168265d906f1fd1f81d0616a9")); // âË_3(íù*
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ public class WBB3Test extends AbstractEncryptionMethodTest {
|
||||
|
||||
public WBB3Test() {
|
||||
super(new WBB3(),
|
||||
new EncryptedPassword("8df818ef7d56075ab2744f74b98ad68a375ccac4", "b7415b355492ea60314f259a35733a3092c03e3f"), // password
|
||||
new EncryptedPassword("106da5cf5df92cb845e12cf62cbdb5235b6dc693", "6110f19b2b52910dccf592a19c59126873f42e69"), // PassWord1
|
||||
new EncryptedPassword("940a9fb7acec0178c6691e8b3c14bd7d789078b1", "f9dd501ff3d1bf74904f9e89649e378429af56e7"), // &^%te$t?Pw@_
|
||||
new EncryptedPassword("0fa12e8d96c9e95f73aa91f3b76f8cdc815ec8a5", "736be8669f6159ddb2d5b47a3e6428cdb8b324de")); // âË_3(íù*
|
||||
new HashedPassword("8df818ef7d56075ab2744f74b98ad68a375ccac4", "b7415b355492ea60314f259a35733a3092c03e3f"), // password
|
||||
new HashedPassword("106da5cf5df92cb845e12cf62cbdb5235b6dc693", "6110f19b2b52910dccf592a19c59126873f42e69"), // PassWord1
|
||||
new HashedPassword("940a9fb7acec0178c6691e8b3c14bd7d789078b1", "f9dd501ff3d1bf74904f9e89649e378429af56e7"), // &^%te$t?Pw@_
|
||||
new HashedPassword("0fa12e8d96c9e95f73aa91f3b76f8cdc815ec8a5", "736be8669f6159ddb2d5b47a3e6428cdb8b324de")); // âË_3(íù*
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user