mirror of
https://github.com/correl/mage.git
synced 2024-12-29 19:15:06 +00:00
Server improves:
* Server: improved messages on register/reset dialogs; * Tests: added database compatible tests on new code or libs (auth db);
This commit is contained in:
parent
ec87af8d9a
commit
301539d75b
8 changed files with 138 additions and 30 deletions
|
@ -232,7 +232,7 @@ public class SessionImpl implements Session {
|
|||
public boolean work() throws Throwable {
|
||||
logger.info("Password reset: reseting password for username " + getUserName());
|
||||
boolean result = server.resetPassword(sessionId, connection.getEmail(), connection.getAuthToken(), connection.getPassword());
|
||||
logger.info("Password reset: " + (result ? "DONE, check your email for new password" : "FAIL"));
|
||||
logger.info("Password reset: " + (result ? "DONE, now you can login with new password" : "FAIL"));
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -22,9 +22,7 @@ import java.io.File;
|
|||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
public enum AuthorizedUserRepository {
|
||||
|
||||
instance;
|
||||
public class AuthorizedUserRepository {
|
||||
|
||||
private static final String JDBC_URL = "jdbc:h2:file:./db/authorized_user.h2;AUTO_SERVER=TRUE";
|
||||
private static final String VERSION_ENTITY_NAME = "authorized_user";
|
||||
|
@ -32,15 +30,20 @@ public enum AuthorizedUserRepository {
|
|||
private static final long DB_VERSION = 2;
|
||||
private static final RandomNumberGenerator rng = new SecureRandomNumberGenerator();
|
||||
|
||||
private static final AuthorizedUserRepository instance;
|
||||
static {
|
||||
instance = new AuthorizedUserRepository(JDBC_URL);
|
||||
}
|
||||
|
||||
private Dao<AuthorizedUser, Object> dao;
|
||||
|
||||
AuthorizedUserRepository() {
|
||||
public AuthorizedUserRepository(String connectionString) {
|
||||
File file = new File("db");
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(connectionString);
|
||||
TableUtils.createTableIfNotExists(connectionSource, AuthorizedUser.class);
|
||||
dao = DaoManager.createDao(connectionSource, AuthorizedUser.class);
|
||||
} catch (SQLException ex) {
|
||||
|
@ -48,6 +51,10 @@ public enum AuthorizedUserRepository {
|
|||
}
|
||||
}
|
||||
|
||||
public static AuthorizedUserRepository getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void add(final String userName, final String password, final String email) {
|
||||
try {
|
||||
Hash hash = new SimpleHash(Sha256Hash.ALGORITHM_NAME, password, rng.nextBytes(), 1024);
|
||||
|
|
|
@ -83,15 +83,17 @@ public class MageServerImpl implements MageServer {
|
|||
@Override
|
||||
public boolean emailAuthToken(String sessionId, String email) throws MageException {
|
||||
if (!managerFactory.configSettings().isAuthenticationActivated()) {
|
||||
sendErrorMessageToClient(sessionId, "Registration is disabled by the server config");
|
||||
sendErrorMessageToClient(sessionId, Session.REGISTRATION_DISABLED_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.instance.getByEmail(email);
|
||||
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.getInstance().getByEmail(email);
|
||||
if (authorizedUser == null) {
|
||||
sendErrorMessageToClient(sessionId, "No user was found with the email address " + email);
|
||||
logger.info("Auth token is requested for " + email + " but there's no such user in DB");
|
||||
return false;
|
||||
}
|
||||
|
||||
String authToken = generateAuthToken();
|
||||
activeAuthTokens.put(email, authToken);
|
||||
String subject = "XMage Password Reset Auth Token";
|
||||
|
@ -113,23 +115,31 @@ public class MageServerImpl implements MageServer {
|
|||
@Override
|
||||
public boolean resetPassword(String sessionId, String email, String authToken, String password) throws MageException {
|
||||
if (!managerFactory.configSettings().isAuthenticationActivated()) {
|
||||
sendErrorMessageToClient(sessionId, "Registration is disabled by the server config");
|
||||
sendErrorMessageToClient(sessionId, Session.REGISTRATION_DISABLED_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// multi-step reset:
|
||||
// - send auth token
|
||||
// - check auth token to confirm reset
|
||||
|
||||
String storedAuthToken = activeAuthTokens.get(email);
|
||||
if (storedAuthToken == null || !storedAuthToken.equals(authToken)) {
|
||||
sendErrorMessageToClient(sessionId, "Invalid auth token");
|
||||
logger.info("Invalid auth token " + authToken + " is sent for " + email);
|
||||
return false;
|
||||
}
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.instance.getByEmail(email);
|
||||
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.getInstance().getByEmail(email);
|
||||
if (authorizedUser == null) {
|
||||
sendErrorMessageToClient(sessionId, "The user is no longer in the DB");
|
||||
sendErrorMessageToClient(sessionId, "User with that email doesn't exists");
|
||||
logger.info("Auth token is valid, but the user with email address " + email + " is no longer in the DB");
|
||||
return false;
|
||||
}
|
||||
AuthorizedUserRepository.instance.remove(authorizedUser.getName());
|
||||
AuthorizedUserRepository.instance.add(authorizedUser.getName(), password, email);
|
||||
|
||||
// recreate user with new password
|
||||
AuthorizedUserRepository.getInstance().remove(authorizedUser.getName());
|
||||
AuthorizedUserRepository.getInstance().add(authorizedUser.getName(), password, email);
|
||||
activeAuthTokens.remove(email);
|
||||
return true;
|
||||
}
|
||||
|
@ -1042,7 +1052,7 @@ public class MageServerImpl implements MageServer {
|
|||
@Override
|
||||
public void setActivation(final String sessionId, final String userName, boolean active) throws MageException {
|
||||
execute("setActivation", sessionId, () -> {
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.instance.getByName(userName);
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.getInstance().getByName(userName);
|
||||
Optional<User> u = managerFactory.userManager().getUserByName(userName);
|
||||
if (u.isPresent()) {
|
||||
User user = u.get();
|
||||
|
|
|
@ -66,7 +66,15 @@ public final class Main {
|
|||
|
||||
public static final PluginClassLoader classLoader = new PluginClassLoader();
|
||||
private static TransporterServer server;
|
||||
|
||||
// special test mode:
|
||||
// - fast game buttons;
|
||||
// - cheat commands;
|
||||
// - no deck validation;
|
||||
// - simplified registration and login (no password check);
|
||||
// - debug main menu for GUI and rendering testing;
|
||||
private static boolean testMode;
|
||||
|
||||
private static boolean fastDbMode;
|
||||
|
||||
/**
|
||||
|
@ -98,7 +106,7 @@ public final class Main {
|
|||
|
||||
if (config.isAuthenticationActivated()) {
|
||||
logger.info("Check authorized user DB version ...");
|
||||
if (!AuthorizedUserRepository.instance.checkAlterAndMigrateAuthorizedUser()) {
|
||||
if (!AuthorizedUserRepository.getInstance().checkAlterAndMigrateAuthorizedUser()) {
|
||||
logger.fatal("Failed to start server.");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,8 @@ public class Session {
|
|||
private static final Pattern alphabetsPattern = Pattern.compile("[a-zA-Z]");
|
||||
private static final Pattern digitsPattern = Pattern.compile("[0-9]");
|
||||
|
||||
public static final String REGISTRATION_DISABLED_MESSAGE = "Registration has been disabled on the server. You can use any name and empty password to login.";
|
||||
|
||||
private final ManagerFactory managerFactory;
|
||||
private final String sessionId;
|
||||
private UUID userId;
|
||||
|
@ -60,30 +62,37 @@ public class Session {
|
|||
|
||||
public String registerUser(String userName, String password, String email) throws MageException {
|
||||
if (!managerFactory.configSettings().isAuthenticationActivated()) {
|
||||
String returnMessage = "Registration is disabled by the server config";
|
||||
String returnMessage = REGISTRATION_DISABLED_MESSAGE;
|
||||
sendErrorMessageToClient(returnMessage);
|
||||
return returnMessage;
|
||||
}
|
||||
synchronized (AuthorizedUserRepository.instance) {
|
||||
synchronized (AuthorizedUserRepository.getInstance()) {
|
||||
// name
|
||||
String returnMessage = validateUserName(userName);
|
||||
if (returnMessage != null) {
|
||||
sendErrorMessageToClient(returnMessage);
|
||||
return returnMessage;
|
||||
}
|
||||
|
||||
// auto-generated password
|
||||
RandomString randomString = new RandomString(10);
|
||||
password = randomString.nextString();
|
||||
returnMessage = validatePassword(password, userName);
|
||||
if (returnMessage != null) {
|
||||
sendErrorMessageToClient(returnMessage);
|
||||
logger.warn("pas: " + password);
|
||||
sendErrorMessageToClient("Auto-generated password fail, try again: " + returnMessage);
|
||||
return returnMessage;
|
||||
}
|
||||
|
||||
// email
|
||||
returnMessage = validateEmail(email);
|
||||
if (returnMessage != null) {
|
||||
sendErrorMessageToClient(returnMessage);
|
||||
return returnMessage;
|
||||
}
|
||||
AuthorizedUserRepository.instance.add(userName, password, email);
|
||||
|
||||
// create
|
||||
AuthorizedUserRepository.getInstance().add(userName, password, email);
|
||||
String text = "You are successfully registered as " + userName + '.';
|
||||
text += " Your initial, generated password is: " + password;
|
||||
|
||||
|
@ -95,15 +104,15 @@ public class Session {
|
|||
success = managerFactory.mailgunClient().sendMessage(email, subject, text);
|
||||
}
|
||||
if (success) {
|
||||
String ok = "Sent a registration confirmation / initial password email to " + email + " for " + userName;
|
||||
String ok = "Email with initial password sent to " + email + " for a user " + userName;
|
||||
logger.info(ok);
|
||||
sendInfoMessageToClient(ok);
|
||||
} else if (Main.isTestMode()) {
|
||||
String ok = "Server is in test mode. Your account is registered with a password of " + password + " for " + userName;
|
||||
String ok = "Email sending failed. Server is in test mode. Your account registered with a password " + password + " for a user " + userName;
|
||||
logger.info(ok);
|
||||
sendInfoMessageToClient(ok);
|
||||
} else {
|
||||
String err = "Failed sending a registration confirmation / initial password email to " + email + " for " + userName;
|
||||
String err = "Email sending failed. Try use another email address or service. Or reset password by email " + email + " for a user " + userName;
|
||||
logger.error(err);
|
||||
sendErrorMessageToClient(err);
|
||||
return err;
|
||||
|
@ -113,9 +122,13 @@ public class Session {
|
|||
}
|
||||
|
||||
private String validateUserName(String userName) {
|
||||
// return error message or null on good name
|
||||
|
||||
if (userName.equals("Admin")) {
|
||||
// virtual user for admin console
|
||||
return "User name Admin already in use";
|
||||
}
|
||||
|
||||
ConfigSettings config = managerFactory.configSettings();
|
||||
if (userName.length() < config.getMinUserNameLength()) {
|
||||
return "User name may not be shorter than " + config.getMinUserNameLength() + " characters";
|
||||
|
@ -123,15 +136,19 @@ public class Session {
|
|||
if (userName.length() > config.getMaxUserNameLength()) {
|
||||
return "User name may not be longer than " + config.getMaxUserNameLength() + " characters";
|
||||
}
|
||||
|
||||
Pattern invalidUserNamePattern = Pattern.compile(managerFactory.configSettings().getInvalidUserNamePattern(), Pattern.CASE_INSENSITIVE);
|
||||
Matcher m = invalidUserNamePattern.matcher(userName);
|
||||
if (m.find()) {
|
||||
return "User name '" + userName + "' includes not allowed characters: use a-z, A-Z and 0-9";
|
||||
}
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.instance.getByName(userName);
|
||||
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.getInstance().getByName(userName);
|
||||
if (authorizedUser != null) {
|
||||
return "User name '" + userName + "' already in use";
|
||||
}
|
||||
|
||||
// all fine
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -159,7 +176,7 @@ public class Session {
|
|||
if (email == null || email.isEmpty()) {
|
||||
return "Email address cannot be blank";
|
||||
}
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.instance.getByEmail(email);
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.getInstance().getByEmail(email);
|
||||
if (authorizedUser != null) {
|
||||
return "Email address '" + email + "' is associated with another user";
|
||||
}
|
||||
|
@ -182,8 +199,8 @@ public class Session {
|
|||
this.isAdmin = false;
|
||||
AuthorizedUser authorizedUser = null;
|
||||
if (managerFactory.configSettings().isAuthenticationActivated()) {
|
||||
authorizedUser = AuthorizedUserRepository.instance.getByName(userName);
|
||||
String errorMsg = "Wrong username or password. In case you haven't, please register your account first.";
|
||||
authorizedUser = AuthorizedUserRepository.getInstance().getByName(userName);
|
||||
String errorMsg = "Wrong username or password. You must register your account first.";
|
||||
if (authorizedUser == null) {
|
||||
return errorMsg;
|
||||
}
|
||||
|
@ -193,16 +210,16 @@ public class Session {
|
|||
}
|
||||
|
||||
if (!authorizedUser.active) {
|
||||
return "Your profile is deactivated, you can't sign on.";
|
||||
return "Your profile has been deactivated by admin.";
|
||||
}
|
||||
if (authorizedUser.lockedUntil != null) {
|
||||
if (authorizedUser.lockedUntil.compareTo(Calendar.getInstance().getTime()) > 0) {
|
||||
return "Your profile is deactivated until " + SystemUtil.dateFormat.format(authorizedUser.lockedUntil);
|
||||
return "Your profile has need deactivated by admin until " + SystemUtil.dateFormat.format(authorizedUser.lockedUntil);
|
||||
} else {
|
||||
// unlock on timeout end
|
||||
managerFactory.userManager().createUser(userName, host, authorizedUser).ifPresent(user
|
||||
-> user.setLockedUntil(null)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -817,7 +817,7 @@ public class User {
|
|||
authorizedUser.chatLockedUntil = this.chatLockedUntil;
|
||||
authorizedUser.lockedUntil = this.lockedUntil;
|
||||
authorizedUser.active = this.active;
|
||||
AuthorizedUserRepository.instance.update(authorizedUser);
|
||||
AuthorizedUserRepository.getInstance().update(authorizedUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
BIN
Mage.Tests/src/test/data/users-db-sample.h2.mv.db
Normal file
BIN
Mage.Tests/src/test/data/users-db-sample.h2.mv.db
Normal file
Binary file not shown.
|
@ -0,0 +1,66 @@
|
|||
package org.mage.test.serverside;
|
||||
|
||||
import mage.server.AuthorizedUser;
|
||||
import mage.server.AuthorizedUserRepository;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* Testing database compatible on new libs or updates.
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class DatabaseCompatibleTest {
|
||||
|
||||
private final String JDBC_URL = "jdbc:h2:file:%s;AUTO_SERVER=TRUE";
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder tempFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void test_AuthUsers() {
|
||||
try {
|
||||
// prepare test db
|
||||
String dbDir = tempFolder.newFolder().getAbsolutePath();
|
||||
String dbName = "users-db-sample.h2";
|
||||
String dbFullName = Paths.get(dbDir, dbName).toAbsolutePath().toString();
|
||||
String dbFullFileName = dbFullName + ".mv.db";
|
||||
Files.copy(
|
||||
Paths.get("src", "test", "data", dbName + ".mv.db"),
|
||||
Paths.get(dbFullFileName)
|
||||
);
|
||||
Assert.assertTrue(Files.exists(Paths.get(dbFullFileName)));
|
||||
|
||||
AuthorizedUserRepository dbUsers = new AuthorizedUserRepository(
|
||||
String.format(JDBC_URL, dbFullName)
|
||||
);
|
||||
|
||||
// search
|
||||
Assert.assertNotNull(dbUsers.getByName("user1"));
|
||||
Assert.assertNotNull(dbUsers.getByEmail("user2@example.com"));
|
||||
Assert.assertNull(dbUsers.getByName("userFAIL"));
|
||||
|
||||
// login
|
||||
AuthorizedUser user = dbUsers.getByName("user3");
|
||||
Assert.assertEquals("user name", user.getName(), "user3");
|
||||
Assert.assertTrue("user pas", user.doCredentialsMatch("user3", "pas3"));
|
||||
Assert.assertFalse("user wrong pas", user.doCredentialsMatch("user3", "123"));
|
||||
Assert.assertFalse("user empty pas", user.doCredentialsMatch("user3", ""));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // TODO: add records/stats db compatible test
|
||||
public void test_Records() {
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue