From 9a9d0974f8720cc36a518bcfaa08bf32f89a5784 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Mon, 20 Mar 2017 08:19:52 +0100 Subject: [PATCH] #1037 Improve architecture of registration methods (#236) * #1037 Improve architecture of registration methods - Introduce parameters classes for each registration method - RegistrationMethod has constants with the required parameters class -> no longer need to have a RegistrationExecutorProvider class that needs to be injected everywhere, let AsyncRegister be the only one to worry about which class will perform the registration - Fix inheritance of password registration types - previously two-factor auth registration inherited from password registration directly * Create matcher which checks for equality via reflections - Allow to perform equality check on objects with default equals() method --- src/main/java/fr/xephi/authme/AuthMe.java | 3 +- src/main/java/fr/xephi/authme/api/NewAPI.java | 12 +- .../executable/register/RegisterCommand.java | 19 +- .../factory/FactoryDependencyHandler.java | 4 +- .../factory/SingletonStore.java | 37 ++++ .../SingletonStoreDependencyHandler.java | 61 +++++++ .../fr/xephi/authme/process/Management.java | 7 +- .../process/register/AsyncRegister.java | 31 ++-- .../AbstractPasswordRegisterExecutor.java | 97 +++++++++++ .../AbstractPasswordRegisterParams.java | 48 +++++ .../ApiPasswordRegisterExecutor.java | 20 +++ .../executors/ApiPasswordRegisterParams.java | 35 ++++ .../executors/EmailRegisterExecutor.java | 80 +++++++++ .../EmailRegisterExecutorProvider.java | 91 ---------- .../executors/EmailRegisterParams.java | 43 +++++ .../executors/PasswordRegisterExecutor.java | 17 ++ .../PasswordRegisterExecutorProvider.java | 164 ------------------ .../executors/PasswordRegisterParams.java | 32 ++++ .../executors/PlayerAuthBuilderHelper.java | 8 + .../executors/RegistrationExecutor.java | 12 +- .../RegistrationExecutorProvider.java | 36 ---- .../executors/RegistrationMethod.java | 51 ++++++ .../executors/RegistrationParameters.java | 28 +++ .../executors/TwoFactorRegisterExecutor.java | 43 +++++ .../executors/TwoFactorRegisterParams.java | 23 +++ .../authme/AuthMeInitializationTest.java | 3 +- .../fr/xephi/authme/ReflectionTestUtils.java | 18 +- .../register/RegisterCommandTest.java | 98 ++++++++--- .../process/register/AsyncRegisterTest.java | 31 +++- .../EmailRegisterExecutorProviderTest.java | 35 ++-- ...java => PasswordRegisterExecutorTest.java} | 26 +-- 31 files changed, 820 insertions(+), 393 deletions(-) create mode 100644 src/main/java/fr/xephi/authme/initialization/factory/SingletonStore.java create mode 100644 src/main/java/fr/xephi/authme/initialization/factory/SingletonStoreDependencyHandler.java create mode 100644 src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterExecutor.java create mode 100644 src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterParams.java create mode 100644 src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterExecutor.java create mode 100644 src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterParams.java create mode 100644 src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutor.java delete mode 100644 src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutorProvider.java create mode 100644 src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterParams.java create mode 100644 src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutor.java delete mode 100644 src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutorProvider.java create mode 100644 src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterParams.java delete mode 100644 src/main/java/fr/xephi/authme/process/register/executors/RegistrationExecutorProvider.java create mode 100644 src/main/java/fr/xephi/authme/process/register/executors/RegistrationMethod.java create mode 100644 src/main/java/fr/xephi/authme/process/register/executors/RegistrationParameters.java create mode 100644 src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterExecutor.java create mode 100644 src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterParams.java rename src/test/java/fr/xephi/authme/process/register/executors/{PasswordRegisterExecutorProviderTest.java => PasswordRegisterExecutorTest.java} (81%) diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 5b21358f..b66258cb 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -15,6 +15,7 @@ import fr.xephi.authme.initialization.OnStartupTasks; import fr.xephi.authme.initialization.SettingsProvider; import fr.xephi.authme.initialization.TaskCloser; import fr.xephi.authme.initialization.factory.FactoryDependencyHandler; +import fr.xephi.authme.initialization.factory.SingletonStoreDependencyHandler; import fr.xephi.authme.listener.BlockListener; import fr.xephi.authme.listener.EntityListener; import fr.xephi.authme.listener.PlayerListener; @@ -206,7 +207,7 @@ public class AuthMe extends JavaPlugin { // Create injector, provide elements from the Bukkit environment and register providers injector = new InjectorBuilder() - .addHandlers(new FactoryDependencyHandler()) + .addHandlers(new FactoryDependencyHandler(), new SingletonStoreDependencyHandler()) .addDefaultHandlers("fr.xephi.authme") .create(); injector.register(AuthMe.class, this); diff --git a/src/main/java/fr/xephi/authme/api/NewAPI.java b/src/main/java/fr/xephi/authme/api/NewAPI.java index 2193f64c..db2c46e0 100644 --- a/src/main/java/fr/xephi/authme/api/NewAPI.java +++ b/src/main/java/fr/xephi/authme/api/NewAPI.java @@ -5,7 +5,8 @@ import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.process.Management; -import fr.xephi.authme.process.register.executors.RegistrationExecutorProvider; +import fr.xephi.authme.process.register.executors.ApiPasswordRegisterParams; +import fr.xephi.authme.process.register.executors.RegistrationMethod; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.service.PluginHookService; @@ -35,15 +36,13 @@ public class NewAPI { private final Management management; private final ValidationService validationService; private final PlayerCache playerCache; - private final RegistrationExecutorProvider registrationExecutorProvider; /* * Constructor for NewAPI. */ @Inject NewAPI(AuthMe plugin, PluginHookService pluginHookService, DataSource dataSource, PasswordSecurity passwordSecurity, - Management management, ValidationService validationService, PlayerCache playerCache, - RegistrationExecutorProvider registrationExecutorProvider) { + Management management, ValidationService validationService, PlayerCache playerCache) { this.plugin = plugin; this.pluginHookService = pluginHookService; this.dataSource = dataSource; @@ -51,7 +50,6 @@ public class NewAPI { this.management = management; this.validationService = validationService; this.playerCache = playerCache; - this.registrationExecutorProvider = registrationExecutorProvider; NewAPI.singleton = this; } @@ -204,8 +202,8 @@ public class NewAPI { * @param autoLogin Should the player be authenticated automatically after the registration? */ public void forceRegister(Player player, String password, boolean autoLogin) { - management.performRegister(player, - registrationExecutorProvider.getPasswordRegisterExecutor(player, password, autoLogin)); + management.performRegister(RegistrationMethod.API_REGISTRATION, + ApiPasswordRegisterParams.of(player, password, autoLogin)); } /** diff --git a/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java b/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java index 6a0a590c..5b9d75ab 100644 --- a/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java @@ -7,7 +7,10 @@ import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.Management; import fr.xephi.authme.process.register.RegisterSecondaryArgument; import fr.xephi.authme.process.register.RegistrationType; -import fr.xephi.authme.process.register.executors.RegistrationExecutorProvider; +import fr.xephi.authme.process.register.executors.EmailRegisterParams; +import fr.xephi.authme.process.register.executors.PasswordRegisterParams; +import fr.xephi.authme.process.register.executors.RegistrationMethod; +import fr.xephi.authme.process.register.executors.TwoFactorRegisterParams; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; @@ -42,15 +45,12 @@ public class RegisterCommand extends PlayerCommand { @Inject private ValidationService validationService; - @Inject - private RegistrationExecutorProvider registrationExecutorProvider; - @Override public void runCommand(Player player, List arguments) { if (commonService.getProperty(SecuritySettings.PASSWORD_HASH) == HashAlgorithm.TWO_FACTOR) { //for two factor auth we don't need to check the usage - management.performRegister(player, - registrationExecutorProvider.getTwoFactorRegisterExecutor(player)); + management.performRegister(RegistrationMethod.TWO_FACTOR_REGISTRATION, + TwoFactorRegisterParams.of(player)); return; } else if (arguments.size() < 1) { commonService.send(player, MessageKey.USAGE_REGISTER); @@ -82,8 +82,8 @@ public class RegisterCommand extends PlayerCommand { final String password = arguments.get(0); final String email = getEmailIfAvailable(arguments); - management.performRegister( - player, registrationExecutorProvider.getPasswordRegisterExecutor(player, password, email)); + management.performRegister(RegistrationMethod.PASSWORD_REGISTRATION, + PasswordRegisterParams.of(player, password, email)); } } @@ -138,7 +138,8 @@ public class RegisterCommand extends PlayerCommand { if (!validationService.validateEmail(email)) { commonService.send(player, MessageKey.INVALID_EMAIL); } else if (isSecondArgValidForEmailRegistration(player, arguments)) { - management.performRegister(player, registrationExecutorProvider.getEmailRegisterExecutor(player, email)); + management.performRegister(RegistrationMethod.EMAIL_REGISTRATION, + EmailRegisterParams.of(player, email)); } } diff --git a/src/main/java/fr/xephi/authme/initialization/factory/FactoryDependencyHandler.java b/src/main/java/fr/xephi/authme/initialization/factory/FactoryDependencyHandler.java index 04c11c68..e063d149 100644 --- a/src/main/java/fr/xephi/authme/initialization/factory/FactoryDependencyHandler.java +++ b/src/main/java/fr/xephi/authme/initialization/factory/FactoryDependencyHandler.java @@ -16,8 +16,8 @@ public class FactoryDependencyHandler implements DependencyHandler { if (dependencyDescription.getType() == Factory.class) { Class genericType = ReflectionUtils.getGenericType(dependencyDescription.getGenericType()); if (genericType == null) { - throw new IllegalStateException("Factory fields must have concrete generic type. " + - "Cannot get generic type for field in '" + context.getMappedClass() + "'"); + throw new IllegalStateException("Factory fields must have concrete generic type. " + + "Cannot get generic type for field in '" + context.getMappedClass() + "'"); } return new FactoryImpl<>(genericType, context.getInjector()); diff --git a/src/main/java/fr/xephi/authme/initialization/factory/SingletonStore.java b/src/main/java/fr/xephi/authme/initialization/factory/SingletonStore.java new file mode 100644 index 00000000..13a5cbb5 --- /dev/null +++ b/src/main/java/fr/xephi/authme/initialization/factory/SingletonStore.java @@ -0,0 +1,37 @@ +package fr.xephi.authme.initialization.factory; + +import java.util.Collection; + +/** + * Injectable object to retrieve and create singletons of a common parent. + * + * @param

the parent class to which this store is limited to + */ +public interface SingletonStore

{ + + /** + * Returns the singleton of the given type, creating it if it hasn't been yet created. + * + * @param clazz the class to get the singleton for + * @param type of the singleton + * @return the singleton of type {@code C} + */ + C getSingleton(Class clazz); + + /** + * Returns all existing singletons of this store's type. + * + * @return all registered singletons of type {@code P} + */ + Collection

retrieveAllOfType(); + + /** + * Returns all existing singletons of the given type. + * + * @param clazz the type to get singletons for + * @param class type + * @return all registered singletons of type {@code C} + */ + Collection retrieveAllOfType(Class clazz); + +} diff --git a/src/main/java/fr/xephi/authme/initialization/factory/SingletonStoreDependencyHandler.java b/src/main/java/fr/xephi/authme/initialization/factory/SingletonStoreDependencyHandler.java new file mode 100644 index 00000000..09a198c7 --- /dev/null +++ b/src/main/java/fr/xephi/authme/initialization/factory/SingletonStoreDependencyHandler.java @@ -0,0 +1,61 @@ +package fr.xephi.authme.initialization.factory; + +import ch.jalu.injector.Injector; +import ch.jalu.injector.context.ResolvedInstantiationContext; +import ch.jalu.injector.handlers.dependency.DependencyHandler; +import ch.jalu.injector.handlers.instantiation.DependencyDescription; +import ch.jalu.injector.utils.ReflectionUtils; + +import java.util.Collection; + +/** + * Dependency handler that builds {@link SingletonStore} objects. + */ +public class SingletonStoreDependencyHandler implements DependencyHandler { + + @Override + public Object resolveValue(ResolvedInstantiationContext context, DependencyDescription dependencyDescription) { + if (dependencyDescription.getType() == SingletonStore.class) { + Class genericType = ReflectionUtils.getGenericType(dependencyDescription.getGenericType()); + if (genericType == null) { + throw new IllegalStateException("Singleton store fields must have concrete generic type. " + + "Cannot get generic type for field in '" + context.getMappedClass() + "'"); + } + + return new SingletonStoreImpl<>(genericType, context.getInjector()); + } + return null; + } + + private static final class SingletonStoreImpl

implements SingletonStore

{ + + private final Injector injector; + private final Class

parentClass; + + SingletonStoreImpl(Class

parentClass, Injector injector) { + this.parentClass = parentClass; + this.injector = injector; + } + + @Override + public C getSingleton(Class clazz) { + if (parentClass.isAssignableFrom(clazz)) { + return injector.getSingleton(clazz); + } + throw new IllegalArgumentException(clazz + " not child of " + parentClass); + } + + @Override + public Collection

retrieveAllOfType() { + return retrieveAllOfType(parentClass); + } + + @Override + public Collection retrieveAllOfType(Class clazz) { + if (parentClass.isAssignableFrom(clazz)) { + return injector.retrieveAllOfType(clazz); + } + throw new IllegalArgumentException(clazz + " not child of " + parentClass); + } + } +} diff --git a/src/main/java/fr/xephi/authme/process/Management.java b/src/main/java/fr/xephi/authme/process/Management.java index 8aea8341..fee94957 100644 --- a/src/main/java/fr/xephi/authme/process/Management.java +++ b/src/main/java/fr/xephi/authme/process/Management.java @@ -8,7 +8,8 @@ import fr.xephi.authme.process.login.AsynchronousLogin; import fr.xephi.authme.process.logout.AsynchronousLogout; import fr.xephi.authme.process.quit.AsynchronousQuit; import fr.xephi.authme.process.register.AsyncRegister; -import fr.xephi.authme.process.register.executors.RegistrationExecutor; +import fr.xephi.authme.process.register.executors.RegistrationMethod; +import fr.xephi.authme.process.register.executors.RegistrationParameters; import fr.xephi.authme.process.unregister.AsynchronousUnregister; import fr.xephi.authme.service.BukkitService; import org.bukkit.command.CommandSender; @@ -60,8 +61,8 @@ public class Management { runTask(() -> asynchronousLogout.logout(player)); } - public void performRegister(Player player, RegistrationExecutor registrationExecutor) { - runTask(() -> asyncRegister.register(player, registrationExecutor)); + public

void performRegister(RegistrationMethod

variant, P parameters) { + runTask(() -> asyncRegister.register(variant, parameters)); } public void performUnregister(Player player, String password) { diff --git a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java index d32f6ead..83e5a82c 100644 --- a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java @@ -3,10 +3,13 @@ package fr.xephi.authme.process.register; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.initialization.factory.SingletonStore; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.process.AsynchronousProcess; import fr.xephi.authme.process.register.executors.RegistrationExecutor; +import fr.xephi.authme.process.register.executors.RegistrationParameters; +import fr.xephi.authme.process.register.executors.RegistrationMethod; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; @@ -31,6 +34,8 @@ public class AsyncRegister implements AsynchronousProcess { private CommonService service; @Inject private PermissionsManager permissionsManager; + @Inject + private SingletonStore registrationExecutorFactory; AsyncRegister() { } @@ -38,12 +43,16 @@ public class AsyncRegister implements AsynchronousProcess { /** * Performs the registration process for the given player. * - * @param player the player to register - * @param executor the registration executor to perform the registration with + * @param variant the registration method + * @param parameters the parameters + * @param

parameters type */ - public void register(Player player, RegistrationExecutor executor) { - if (preRegisterCheck(player) && executor.isRegistrationAdmitted()) { - executeRegistration(player, executor); + public

void register(RegistrationMethod

variant, P parameters) { + if (preRegisterCheck(parameters.getPlayer())) { + RegistrationExecutor

executor = registrationExecutorFactory.getSingleton(variant.getExecutorClass()); + if (executor.isRegistrationAdmitted(parameters)) { + executeRegistration(parameters, executor); + } } } @@ -66,15 +75,17 @@ public class AsyncRegister implements AsynchronousProcess { /** * Executes the registration. * - * @param player the player to register + * @param parameters the registration parameters * @param executor the executor to perform the registration process with + * @param

registration params type */ - private void executeRegistration(Player player, RegistrationExecutor executor) { - PlayerAuth auth = executor.buildPlayerAuth(); + private

+ void executeRegistration(P parameters, RegistrationExecutor

executor) { + PlayerAuth auth = executor.buildPlayerAuth(parameters); if (database.saveAuth(auth)) { - executor.executePostPersistAction(); + executor.executePostPersistAction(parameters); } else { - service.send(player, MessageKey.ERROR); + service.send(parameters.getPlayer(), MessageKey.ERROR); } } diff --git a/src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterExecutor.java b/src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterExecutor.java new file mode 100644 index 00000000..9f0ee724 --- /dev/null +++ b/src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterExecutor.java @@ -0,0 +1,97 @@ +package fr.xephi.authme.process.register.executors; + +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.process.SyncProcessManager; +import fr.xephi.authme.process.login.AsynchronousLogin; +import fr.xephi.authme.security.PasswordSecurity; +import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.service.BukkitService; +import fr.xephi.authme.service.CommonService; +import fr.xephi.authme.service.ValidationService; +import fr.xephi.authme.settings.properties.PluginSettings; +import fr.xephi.authme.settings.properties.RegistrationSettings; +import org.bukkit.entity.Player; + +import javax.inject.Inject; + +/** + * Registration executor for registration methods where the password + * is supplied by the user. + */ +abstract class AbstractPasswordRegisterExecutor

+ implements RegistrationExecutor

{ + + /** + * Number of ticks to wait before running the login action when it is run synchronously. + * A small delay is necessary or the database won't return the newly saved PlayerAuth object + * and the login process thinks the user is not registered. + */ + private static final int SYNC_LOGIN_DELAY = 5; + + @Inject + private ValidationService validationService; + + @Inject + private CommonService commonService; + + @Inject + private PasswordSecurity passwordSecurity; + + @Inject + private BukkitService bukkitService; + + @Inject + private SyncProcessManager syncProcessManager; + + @Inject + private AsynchronousLogin asynchronousLogin; + + @Override + public boolean isRegistrationAdmitted(P params) { + ValidationService.ValidationResult passwordValidation = validationService.validatePassword( + params.getPassword(), params.getPlayer().getName()); + if (passwordValidation.hasError()) { + commonService.send(params.getPlayer(), passwordValidation.getMessageKey(), passwordValidation.getArgs()); + return false; + } + return true; + } + + @Override + public PlayerAuth buildPlayerAuth(P params) { + HashedPassword hashedPassword = passwordSecurity.computeHash(params.getPassword(), params.getPlayerName()); + params.setHashedPassword(hashedPassword); + return createPlayerAuthObject(params); + } + + /** + * Creates the PlayerAuth object to store into the database, based on the registration parameters. + * + * @param params the parameters + * @return the PlayerAuth representing the new account to register + */ + protected abstract PlayerAuth createPlayerAuthObject(P params); + + /** + * Returns whether the player should be automatically logged in after registration. + * + * @param params the registration parameters + * @return true if the player should be logged in, false otherwise + */ + protected boolean performLoginAfterRegister(P params) { + return !commonService.getProperty(RegistrationSettings.FORCE_LOGIN_AFTER_REGISTER); + } + + @Override + public void executePostPersistAction(P params) { + final Player player = params.getPlayer(); + if (performLoginAfterRegister(params)) { + if (commonService.getProperty(PluginSettings.USE_ASYNC_TASKS)) { + bukkitService.runTaskAsynchronously(() -> asynchronousLogin.forceLogin(player)); + } else { + bukkitService.scheduleSyncDelayedTask(() -> asynchronousLogin.forceLogin(player), SYNC_LOGIN_DELAY); + } + } + syncProcessManager.processSyncPasswordRegister(player); + } +} diff --git a/src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterParams.java b/src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterParams.java new file mode 100644 index 00000000..0c7a1d51 --- /dev/null +++ b/src/main/java/fr/xephi/authme/process/register/executors/AbstractPasswordRegisterParams.java @@ -0,0 +1,48 @@ +package fr.xephi.authme.process.register.executors; + +import fr.xephi.authme.security.crypts.HashedPassword; +import org.bukkit.entity.Player; + +/** + * Common params type for implementors of {@link AbstractPasswordRegisterExecutor}. + * Password must be supplied on creation and cannot be changed later on. The {@link HashedPassword} + * is stored on the params object for later use. + */ +public abstract class AbstractPasswordRegisterParams extends RegistrationParameters { + + private final String password; + private HashedPassword hashedPassword; + + /** + * Constructor. + * + * @param player the player to register + * @param password the password to use + */ + public AbstractPasswordRegisterParams(Player player, String password) { + super(player); + this.password = password; + } + + /** + * Constructor with no defined password. Use for registration methods which + * have no implicit password (like two factor authentication). + * + * @param player the player to register + */ + public AbstractPasswordRegisterParams(Player player) { + this(player, null); + } + + public String getPassword() { + return password; + } + + void setHashedPassword(HashedPassword hashedPassword) { + this.hashedPassword = hashedPassword; + } + + HashedPassword getHashedPassword() { + return hashedPassword; + } +} diff --git a/src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterExecutor.java b/src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterExecutor.java new file mode 100644 index 00000000..6005c8e4 --- /dev/null +++ b/src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterExecutor.java @@ -0,0 +1,20 @@ +package fr.xephi.authme.process.register.executors; + +import fr.xephi.authme.data.auth.PlayerAuth; + +/** + * Executor for password registration via API call. + */ +class ApiPasswordRegisterExecutor extends AbstractPasswordRegisterExecutor { + + @Override + protected PlayerAuth createPlayerAuthObject(ApiPasswordRegisterParams params) { + return PlayerAuthBuilderHelper + .createPlayerAuth(params.getPlayer(), params.getHashedPassword(), null); + } + + @Override + protected boolean performLoginAfterRegister(ApiPasswordRegisterParams params) { + return params.getLoginAfterRegister(); + } +} diff --git a/src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterParams.java b/src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterParams.java new file mode 100644 index 00000000..357d32c2 --- /dev/null +++ b/src/main/java/fr/xephi/authme/process/register/executors/ApiPasswordRegisterParams.java @@ -0,0 +1,35 @@ +package fr.xephi.authme.process.register.executors; + +import org.bukkit.entity.Player; + +/** + * Parameters for {@link ApiPasswordRegisterExecutor}. + */ +public class ApiPasswordRegisterParams extends PasswordRegisterParams { + + private final boolean loginAfterRegister; + + protected ApiPasswordRegisterParams(Player player, String password, boolean loginAfterRegister) { + super(player, password, null); + this.loginAfterRegister = loginAfterRegister; + } + + /** + * Creates a parameters object. + * + * @param player the player to register + * @param password the password to register with + * @param loginAfterRegister whether the player should be logged in after registration + * @return params object with the given data + */ + public static ApiPasswordRegisterParams of(Player player, String password, boolean loginAfterRegister) { + return new ApiPasswordRegisterParams(player, password, loginAfterRegister); + } + + /** + * @return true if the player should be logged in after being registered, false otherwise + */ + public boolean getLoginAfterRegister() { + return loginAfterRegister; + } +} diff --git a/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutor.java b/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutor.java new file mode 100644 index 00000000..0f4153b1 --- /dev/null +++ b/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutor.java @@ -0,0 +1,80 @@ +package fr.xephi.authme.process.register.executors; + +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.mail.EmailService; +import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.process.SyncProcessManager; +import fr.xephi.authme.security.PasswordSecurity; +import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.service.CommonService; +import fr.xephi.authme.settings.properties.EmailSettings; +import fr.xephi.authme.util.RandomStringUtils; +import org.bukkit.entity.Player; + +import javax.inject.Inject; + +import static fr.xephi.authme.permission.PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS; +import static fr.xephi.authme.process.register.executors.PlayerAuthBuilderHelper.createPlayerAuth; +import static fr.xephi.authme.settings.properties.EmailSettings.RECOVERY_PASSWORD_LENGTH; + +/** + * Executor for email registration: the player only provides his email address, + * to which a generated password is sent. + */ +class EmailRegisterExecutor implements RegistrationExecutor { + + @Inject + private PermissionsManager permissionsManager; + + @Inject + private DataSource dataSource; + + @Inject + private CommonService commonService; + + @Inject + private EmailService emailService; + + @Inject + private SyncProcessManager syncProcessManager; + + @Inject + private PasswordSecurity passwordSecurity; + + @Override + public boolean isRegistrationAdmitted(EmailRegisterParams params) { + final int maxRegPerEmail = commonService.getProperty(EmailSettings.MAX_REG_PER_EMAIL); + if (maxRegPerEmail > 0 && !permissionsManager.hasPermission(params.getPlayer(), ALLOW_MULTIPLE_ACCOUNTS)) { + int otherAccounts = dataSource.countAuthsByEmail(params.getEmail()); + if (otherAccounts >= maxRegPerEmail) { + commonService.send(params.getPlayer(), MessageKey.MAX_REGISTER_EXCEEDED, + Integer.toString(maxRegPerEmail), Integer.toString(otherAccounts), "@"); + return false; + } + } + return true; + } + + @Override + public PlayerAuth buildPlayerAuth(EmailRegisterParams params) { + String password = RandomStringUtils.generate(commonService.getProperty(RECOVERY_PASSWORD_LENGTH)); + HashedPassword hashedPassword = passwordSecurity.computeHash(password, params.getPlayer().getName()); + params.setPassword(password); + return createPlayerAuth(params.getPlayer(), hashedPassword, params.getEmail()); + } + + @Override + public void executePostPersistAction(EmailRegisterParams params) { + Player player = params.getPlayer(); + boolean couldSendMail = emailService.sendPasswordMail( + player.getName(), params.getEmail(), params.getPassword()); + if (couldSendMail) { + syncProcessManager.processSyncEmailRegister(player); + } else { + commonService.send(player, MessageKey.EMAIL_SEND_FAILURE); + } + } + +} diff --git a/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutorProvider.java b/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutorProvider.java deleted file mode 100644 index 7a6e702d..00000000 --- a/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutorProvider.java +++ /dev/null @@ -1,91 +0,0 @@ -package fr.xephi.authme.process.register.executors; - -import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.mail.EmailService; -import fr.xephi.authme.message.MessageKey; -import fr.xephi.authme.permission.PermissionsManager; -import fr.xephi.authme.process.SyncProcessManager; -import fr.xephi.authme.security.PasswordSecurity; -import fr.xephi.authme.security.crypts.HashedPassword; -import fr.xephi.authme.service.CommonService; -import fr.xephi.authme.settings.properties.EmailSettings; -import fr.xephi.authme.util.RandomStringUtils; -import org.bukkit.entity.Player; - -import javax.inject.Inject; - -import static fr.xephi.authme.permission.PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS; -import static fr.xephi.authme.process.register.executors.PlayerAuthBuilderHelper.createPlayerAuth; -import static fr.xephi.authme.settings.properties.EmailSettings.RECOVERY_PASSWORD_LENGTH; - -/** - * Provides a registration executor for email registration. - */ -class EmailRegisterExecutorProvider { - - @Inject - private PermissionsManager permissionsManager; - - @Inject - private DataSource dataSource; - - @Inject - private CommonService commonService; - - @Inject - private EmailService emailService; - - @Inject - private SyncProcessManager syncProcessManager; - - @Inject - private PasswordSecurity passwordSecurity; - - EmailRegisterExecutorProvider() { - } - - /** Registration executor implementation for email registration. */ - class EmailRegisterExecutor implements RegistrationExecutor { - - private final Player player; - private final String email; - private String password; - - EmailRegisterExecutor(Player player, String email) { - this.player = player; - this.email = email; - } - - @Override - public boolean isRegistrationAdmitted() { - final int maxRegPerEmail = commonService.getProperty(EmailSettings.MAX_REG_PER_EMAIL); - if (maxRegPerEmail > 0 && !permissionsManager.hasPermission(player, ALLOW_MULTIPLE_ACCOUNTS)) { - int otherAccounts = dataSource.countAuthsByEmail(email); - if (otherAccounts >= maxRegPerEmail) { - commonService.send(player, MessageKey.MAX_REGISTER_EXCEEDED, Integer.toString(maxRegPerEmail), - Integer.toString(otherAccounts), "@"); - return false; - } - } - return true; - } - - @Override - public PlayerAuth buildPlayerAuth() { - password = RandomStringUtils.generate(commonService.getProperty(RECOVERY_PASSWORD_LENGTH)); - HashedPassword hashedPassword = passwordSecurity.computeHash(password, player.getName()); - return createPlayerAuth(player, hashedPassword, email); - } - - @Override - public void executePostPersistAction() { - boolean couldSendMail = emailService.sendPasswordMail(player.getName(), email, password); - if (couldSendMail) { - syncProcessManager.processSyncEmailRegister(player); - } else { - commonService.send(player, MessageKey.EMAIL_SEND_FAILURE); - } - } - } -} diff --git a/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterParams.java b/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterParams.java new file mode 100644 index 00000000..94d03acc --- /dev/null +++ b/src/main/java/fr/xephi/authme/process/register/executors/EmailRegisterParams.java @@ -0,0 +1,43 @@ +package fr.xephi.authme.process.register.executors; + +import org.bukkit.entity.Player; + +/** + * Parameters for email registration. + */ +public class EmailRegisterParams extends RegistrationParameters { + + private final String email; + private String password; + + protected EmailRegisterParams(Player player, String email) { + super(player); + this.email = email; + } + + /** + * Creates a params object for email registration. + * + * @param player the player to register + * @param email the player's email + * @return params object with the given data + */ + public static EmailRegisterParams of(Player player, String email) { + return new EmailRegisterParams(player, email); + } + + public String getEmail() { + return email; + } + + void setPassword(String password) { + this.password = password; + } + + /** + * @return the password generated for the player + */ + String getPassword() { + return password; + } +} diff --git a/src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutor.java b/src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutor.java new file mode 100644 index 00000000..167bee29 --- /dev/null +++ b/src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutor.java @@ -0,0 +1,17 @@ +package fr.xephi.authme.process.register.executors; + +import fr.xephi.authme.data.auth.PlayerAuth; + +import static fr.xephi.authme.process.register.executors.PlayerAuthBuilderHelper.createPlayerAuth; + +/** + * Registration executor for password registration. + */ +class PasswordRegisterExecutor extends AbstractPasswordRegisterExecutor { + + @Override + public PlayerAuth createPlayerAuthObject(PasswordRegisterParams params) { + return createPlayerAuth(params.getPlayer(), params.getHashedPassword(), params.getEmail()); + } + +} diff --git a/src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutorProvider.java b/src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutorProvider.java deleted file mode 100644 index 9d40bcf8..00000000 --- a/src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutorProvider.java +++ /dev/null @@ -1,164 +0,0 @@ -package fr.xephi.authme.process.register.executors; - -import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.message.MessageKey; -import fr.xephi.authme.process.SyncProcessManager; -import fr.xephi.authme.process.login.AsynchronousLogin; -import fr.xephi.authme.security.PasswordSecurity; -import fr.xephi.authme.security.crypts.HashedPassword; -import fr.xephi.authme.security.crypts.TwoFactor; -import fr.xephi.authme.service.BukkitService; -import fr.xephi.authme.service.CommonService; -import fr.xephi.authme.service.ValidationService; -import fr.xephi.authme.service.ValidationService.ValidationResult; -import fr.xephi.authme.settings.properties.PluginSettings; -import fr.xephi.authme.settings.properties.RegistrationSettings; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -import javax.inject.Inject; - -import static fr.xephi.authme.process.register.executors.PlayerAuthBuilderHelper.createPlayerAuth; - -/** - * Provides registration executors for password-based registration variants. - */ -class PasswordRegisterExecutorProvider { - - /** - * Number of ticks to wait before running the login action when it is run synchronously. - * A small delay is necessary or the database won't return the newly saved PlayerAuth object - * and the login process thinks the user is not registered. - */ - private static final int SYNC_LOGIN_DELAY = 5; - - @Inject - private ValidationService validationService; - - @Inject - private CommonService commonService; - - @Inject - private PasswordSecurity passwordSecurity; - - @Inject - private BukkitService bukkitService; - - @Inject - private SyncProcessManager syncProcessManager; - - @Inject - private AsynchronousLogin asynchronousLogin; - - PasswordRegisterExecutorProvider() { - } - - /** Registration executor for password registration. */ - class PasswordRegisterExecutor implements RegistrationExecutor { - - private final Player player; - private final String password; - private final String email; - private HashedPassword hashedPassword; - - /** - * Constructor. - * - * @param player the player to register - * @param password the password to register with - * @param email the email of the player (may be null) - */ - PasswordRegisterExecutor(Player player, String password, String email) { - this.player = player; - this.password = password; - this.email = email; - } - - @Override - public boolean isRegistrationAdmitted() { - ValidationResult passwordValidation = validationService.validatePassword(password, player.getName()); - if (passwordValidation.hasError()) { - commonService.send(player, passwordValidation.getMessageKey(), passwordValidation.getArgs()); - return false; - } - return true; - } - - @Override - public PlayerAuth buildPlayerAuth() { - hashedPassword = passwordSecurity.computeHash(password, player.getName().toLowerCase()); - return createPlayerAuth(player, hashedPassword, email); - } - - protected boolean performLoginAfterRegister() { - return !commonService.getProperty(RegistrationSettings.FORCE_LOGIN_AFTER_REGISTER); - } - - @Override - public void executePostPersistAction() { - if (performLoginAfterRegister()) { - if (commonService.getProperty(PluginSettings.USE_ASYNC_TASKS)) { - bukkitService.runTaskAsynchronously(() -> asynchronousLogin.forceLogin(player)); - } else { - bukkitService.scheduleSyncDelayedTask(() -> asynchronousLogin.forceLogin(player), SYNC_LOGIN_DELAY); - } - } - syncProcessManager.processSyncPasswordRegister(player); - } - - protected Player getPlayer() { - return player; - } - - protected HashedPassword getHashedPassword() { - return hashedPassword; - } - } - - /** Executor for password registration via API call. */ - class ApiPasswordRegisterExecutor extends PasswordRegisterExecutor { - - private final boolean loginAfterRegister; - - /** - * Constructor. - * - * @param player the player to register - * @param password the password to register with - * @param loginAfterRegister whether the user should be automatically logged in after registration - */ - ApiPasswordRegisterExecutor(Player player, String password, boolean loginAfterRegister) { - super(player, password, null); - this.loginAfterRegister = loginAfterRegister; - } - - @Override - protected boolean performLoginAfterRegister() { - return loginAfterRegister; - } - } - - /** Executor for two factor registration. */ - class TwoFactorRegisterExecutor extends PasswordRegisterExecutor { - - TwoFactorRegisterExecutor(Player player) { - super(player, "", null); - } - - @Override - public boolean isRegistrationAdmitted() { - // nothing to check - return true; - } - - @Override - public void executePostPersistAction() { - super.executePostPersistAction(); - - String hash = getHashedPassword().getHash(); - String qrCodeUrl = TwoFactor.getQRBarcodeURL(getPlayer().getName(), Bukkit.getIp(), hash); - commonService.send(getPlayer(), MessageKey.TWO_FACTOR_CREATE, hash, qrCodeUrl); - } - - } -} diff --git a/src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterParams.java b/src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterParams.java new file mode 100644 index 00000000..f21861bf --- /dev/null +++ b/src/main/java/fr/xephi/authme/process/register/executors/PasswordRegisterParams.java @@ -0,0 +1,32 @@ +package fr.xephi.authme.process.register.executors; + +import org.bukkit.entity.Player; + +/** + * Parameters for registration with a given password, and optionally an email address. + */ +public class PasswordRegisterParams extends AbstractPasswordRegisterParams { + + private final String email; + + protected PasswordRegisterParams(Player player, String password, String email) { + super(player, password); + this.email = email; + } + + /** + * Creates a params object. + * + * @param player the player to register + * @param password the password to register with + * @param email the email of the player (may be null) + * @return params object with the given data + */ + public static PasswordRegisterParams of(Player player, String password, String email) { + return new PasswordRegisterParams(player, password, email); + } + + public String getEmail() { + return email; + } +} diff --git a/src/main/java/fr/xephi/authme/process/register/executors/PlayerAuthBuilderHelper.java b/src/main/java/fr/xephi/authme/process/register/executors/PlayerAuthBuilderHelper.java index 1943b5d5..5d9ab865 100644 --- a/src/main/java/fr/xephi/authme/process/register/executors/PlayerAuthBuilderHelper.java +++ b/src/main/java/fr/xephi/authme/process/register/executors/PlayerAuthBuilderHelper.java @@ -13,6 +13,14 @@ final class PlayerAuthBuilderHelper { private PlayerAuthBuilderHelper() { } + /** + * Creates a {@link PlayerAuth} object with the given data. + * + * @param player the player to create a PlayerAuth for + * @param hashedPassword the hashed password + * @param email the email address (nullable) + * @return the generated PlayerAuth object + */ static PlayerAuth createPlayerAuth(Player player, HashedPassword hashedPassword, String email) { return PlayerAuth.builder() .name(player.getName().toLowerCase()) diff --git a/src/main/java/fr/xephi/authme/process/register/executors/RegistrationExecutor.java b/src/main/java/fr/xephi/authme/process/register/executors/RegistrationExecutor.java index cceb5c18..ad18bf92 100644 --- a/src/main/java/fr/xephi/authme/process/register/executors/RegistrationExecutor.java +++ b/src/main/java/fr/xephi/authme/process/register/executors/RegistrationExecutor.java @@ -5,7 +5,7 @@ import fr.xephi.authme.data.auth.PlayerAuth; /** * Performs the registration action. */ -public interface RegistrationExecutor { +public interface RegistrationExecutor

{ /** * Returns whether the registration may take place. Use this method to execute @@ -14,20 +14,24 @@ public interface RegistrationExecutor { * If this method returns {@code false}, it is expected that the executor inform * the player about the error within this method call. * + * @param params the parameters for the registration * @return true if registration may be performed, false otherwise */ - boolean isRegistrationAdmitted(); + boolean isRegistrationAdmitted(P params); /** * Constructs the PlayerAuth object to persist into the database. * + * @param params the parameters for the registration * @return the player auth to register in the data source */ - PlayerAuth buildPlayerAuth(); + PlayerAuth buildPlayerAuth(P params); /** * Follow-up method called after the player auth could be added into the database. + * + * @param params the parameters for the registration */ - void executePostPersistAction(); + void executePostPersistAction(P params); } diff --git a/src/main/java/fr/xephi/authme/process/register/executors/RegistrationExecutorProvider.java b/src/main/java/fr/xephi/authme/process/register/executors/RegistrationExecutorProvider.java deleted file mode 100644 index 286a5163..00000000 --- a/src/main/java/fr/xephi/authme/process/register/executors/RegistrationExecutorProvider.java +++ /dev/null @@ -1,36 +0,0 @@ -package fr.xephi.authme.process.register.executors; - -import org.bukkit.entity.Player; - -import javax.inject.Inject; - -/** - * Provides a {@link RegistrationExecutor} for various registration methods. - */ -public class RegistrationExecutorProvider { - - @Inject - private PasswordRegisterExecutorProvider passwordRegisterExecutorProvider; - - @Inject - private EmailRegisterExecutorProvider emailRegisterExecutorProvider; - - RegistrationExecutorProvider() { - } - - public RegistrationExecutor getPasswordRegisterExecutor(Player player, String password, String email) { - return passwordRegisterExecutorProvider.new PasswordRegisterExecutor(player, password, email); - } - - public RegistrationExecutor getPasswordRegisterExecutor(Player player, String password, boolean loginAfterRegister) { - return passwordRegisterExecutorProvider.new ApiPasswordRegisterExecutor(player, password, loginAfterRegister); - } - - public RegistrationExecutor getTwoFactorRegisterExecutor(Player player) { - return passwordRegisterExecutorProvider.new TwoFactorRegisterExecutor(player); - } - - public RegistrationExecutor getEmailRegisterExecutor(Player player, String email) { - return emailRegisterExecutorProvider.new EmailRegisterExecutor(player, email); - } -} diff --git a/src/main/java/fr/xephi/authme/process/register/executors/RegistrationMethod.java b/src/main/java/fr/xephi/authme/process/register/executors/RegistrationMethod.java new file mode 100644 index 00000000..9232332e --- /dev/null +++ b/src/main/java/fr/xephi/authme/process/register/executors/RegistrationMethod.java @@ -0,0 +1,51 @@ +package fr.xephi.authme.process.register.executors; + +/** + * Methods with which a player can be registered. + *

+ * These constants each define a different way of registering a player and define the + * {@link RegistrationParameters parameters} and {@link RegistrationExecutor executor} + * classes which perform this registration method. This is essentially a typed enum + * as passing a constant of this class along with a parameters object to a method can + * be restricted to the correct parameters type. + */ +public final class RegistrationMethod

{ + + /** + * Password registration. + */ + public static final RegistrationMethod PASSWORD_REGISTRATION = + new RegistrationMethod<>(PasswordRegisterExecutor.class); + + /** + * Registration with two-factor authentication as login means. + */ + public static final RegistrationMethod TWO_FACTOR_REGISTRATION = + new RegistrationMethod<>(TwoFactorRegisterExecutor.class); + + /** + * Email registration: an email address is provided, to which a generated password is sent. + */ + public static final RegistrationMethod EMAIL_REGISTRATION = + new RegistrationMethod<>(EmailRegisterExecutor.class); + + /** + * API registration: player and password are provided via an API method. + */ + public static final RegistrationMethod API_REGISTRATION = + new RegistrationMethod<>(ApiPasswordRegisterExecutor.class); + + + private final Class> executorClass; + + private RegistrationMethod(Class> executorClass) { + this.executorClass = executorClass; + } + + /** + * @return the executor class to perform the registration method + */ + public Class> getExecutorClass() { + return executorClass; + } +} diff --git a/src/main/java/fr/xephi/authme/process/register/executors/RegistrationParameters.java b/src/main/java/fr/xephi/authme/process/register/executors/RegistrationParameters.java new file mode 100644 index 00000000..c92d57ff --- /dev/null +++ b/src/main/java/fr/xephi/authme/process/register/executors/RegistrationParameters.java @@ -0,0 +1,28 @@ +package fr.xephi.authme.process.register.executors; + +import org.bukkit.entity.Player; + +/** + * Parent of all registration parameters. + */ +public abstract class RegistrationParameters { + + private final Player player; + + /** + * Constructor. + * + * @param player the player to perform the registration for + */ + public RegistrationParameters(Player player) { + this.player = player; + } + + public Player getPlayer() { + return player; + } + + public String getPlayerName() { + return player.getName(); + } +} diff --git a/src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterExecutor.java b/src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterExecutor.java new file mode 100644 index 00000000..027a5fa6 --- /dev/null +++ b/src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterExecutor.java @@ -0,0 +1,43 @@ +package fr.xephi.authme.process.register.executors; + +import fr.xephi.authme.data.auth.PlayerAuth; +import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.security.crypts.TwoFactor; +import fr.xephi.authme.service.CommonService; +import org.bukkit.Bukkit; + +import javax.inject.Inject; + +import static fr.xephi.authme.process.register.executors.PlayerAuthBuilderHelper.createPlayerAuth; + +/** + * Executor for two-factor registration. + */ +class TwoFactorRegisterExecutor extends AbstractPasswordRegisterExecutor { + + @Inject + private CommonService commonService; + + @Override + public boolean isRegistrationAdmitted(TwoFactorRegisterParams params) { + // nothing to check + return true; + } + + @Override + protected PlayerAuth createPlayerAuthObject(TwoFactorRegisterParams params) { + return createPlayerAuth(params.getPlayer(), params.getHashedPassword(), null); + } + + @Override + public void executePostPersistAction(TwoFactorRegisterParams params) { + super.executePostPersistAction(params); + + // Note ljacqu 20170317: This two-factor registration type is only invoked when the password hash is configured + // to two-factor authentication. Therefore, the hashed password is the result of the TwoFactor EncryptionMethod + // implementation (contains the TOTP secret). + String hash = params.getHashedPassword().getHash(); + String qrCodeUrl = TwoFactor.getQRBarcodeURL(params.getPlayerName(), Bukkit.getIp(), hash); + commonService.send(params.getPlayer(), MessageKey.TWO_FACTOR_CREATE, hash, qrCodeUrl); + } +} diff --git a/src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterParams.java b/src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterParams.java new file mode 100644 index 00000000..a7a75875 --- /dev/null +++ b/src/main/java/fr/xephi/authme/process/register/executors/TwoFactorRegisterParams.java @@ -0,0 +1,23 @@ +package fr.xephi.authme.process.register.executors; + +import org.bukkit.entity.Player; + +/** + * Parameters for registration with two-factor authentication. + */ +public class TwoFactorRegisterParams extends AbstractPasswordRegisterParams { + + protected TwoFactorRegisterParams(Player player) { + super(player); + } + + /** + * Creates a parameters object. + * + * @param player the player to register + * @return params object with the given player + */ + public static TwoFactorRegisterParams of(Player player) { + return new TwoFactorRegisterParams(player); + } +} diff --git a/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java b/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java index 89132fcf..f11d7311 100644 --- a/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java +++ b/src/test/java/fr/xephi/authme/AuthMeInitializationTest.java @@ -9,6 +9,7 @@ import fr.xephi.authme.command.CommandHandler; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.initialization.factory.FactoryDependencyHandler; +import fr.xephi.authme.initialization.factory.SingletonStoreDependencyHandler; import fr.xephi.authme.listener.BlockListener; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.process.Management; @@ -93,7 +94,7 @@ public class AuthMeInitializationTest { new Settings(dataFolder, mock(PropertyResource.class), null, buildConfigurationData()); Injector injector = new InjectorBuilder() - .addHandlers(new FactoryDependencyHandler()) + .addHandlers(new FactoryDependencyHandler(), new SingletonStoreDependencyHandler()) .addDefaultHandlers("fr.xephi.authme") .create(); injector.provide(DataFolder.class, dataFolder); diff --git a/src/test/java/fr/xephi/authme/ReflectionTestUtils.java b/src/test/java/fr/xephi/authme/ReflectionTestUtils.java index fdd716bd..a4392a32 100644 --- a/src/test/java/fr/xephi/authme/ReflectionTestUtils.java +++ b/src/test/java/fr/xephi/authme/ReflectionTestUtils.java @@ -25,7 +25,7 @@ public final class ReflectionTestUtils { */ public static void setField(Class clazz, T instance, String fieldName, Object value) { try { - Field field = getField(clazz, instance, fieldName); + Field field = getField(clazz, fieldName); field.set(instance, value); } catch (IllegalAccessException e) { throw new UnsupportedOperationException( @@ -34,24 +34,30 @@ public final class ReflectionTestUtils { } } - private static Field getField(Class clazz, T instance, String fieldName) { + private static Field getField(Class clazz, String fieldName) { try { Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); return field; } catch (NoSuchFieldException e) { - throw new UnsupportedOperationException(format("Could not get field '%s' for instance '%s' of class '%s'", - fieldName, instance, clazz.getName()), e); + throw new UnsupportedOperationException(format("Could not get field '%s' from class '%s'", + fieldName, clazz.getName()), e); } } @SuppressWarnings("unchecked") public static V getFieldValue(Class clazz, T instance, String fieldName) { - Field field = getField(clazz, instance, fieldName); + Field field = getField(clazz, fieldName); + return getFieldValue(field, instance); + } + + @SuppressWarnings("unchecked") + public static V getFieldValue(Field field, Object instance) { + field.setAccessible(true); try { return (V) field.get(instance); } catch (IllegalAccessException e) { - throw new UnsupportedOperationException("Could not get value of field '" + fieldName + "'", e); + throw new UnsupportedOperationException("Could not get value of field '" + field.getName() + "'", e); } } diff --git a/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java index c87252e1..6224a606 100644 --- a/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/register/RegisterCommandTest.java @@ -1,13 +1,17 @@ package fr.xephi.authme.command.executable.register; +import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; import fr.xephi.authme.mail.EmailService; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.Management; import fr.xephi.authme.process.register.RegisterSecondaryArgument; import fr.xephi.authme.process.register.RegistrationType; -import fr.xephi.authme.process.register.executors.RegistrationExecutor; -import fr.xephi.authme.process.register.executors.RegistrationExecutorProvider; +import fr.xephi.authme.process.register.executors.EmailRegisterParams; +import fr.xephi.authme.process.register.executors.PasswordRegisterParams; +import fr.xephi.authme.process.register.executors.RegistrationMethod; +import fr.xephi.authme.process.register.executors.RegistrationParameters; +import fr.xephi.authme.process.register.executors.TwoFactorRegisterParams; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; @@ -16,6 +20,9 @@ import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -24,10 +31,16 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.List; +import java.util.Objects; import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; @@ -57,9 +70,6 @@ public class RegisterCommandTest { @Mock private ValidationService validationService; - @Mock - private RegistrationExecutorProvider registrationExecutorProvider; - @BeforeClass public static void setup() { TestHelper.setupLogger(); @@ -90,14 +100,13 @@ public class RegisterCommandTest { // given given(commonService.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(HashAlgorithm.TWO_FACTOR); Player player = mock(Player.class); - RegistrationExecutor executor = mock(RegistrationExecutor.class); - given(registrationExecutorProvider.getTwoFactorRegisterExecutor(player)).willReturn(executor); // when command.executeCommand(player, Collections.emptyList()); // then - verify(management).performRegister(player, executor); + verify(management).performRegister(eq(RegistrationMethod.TWO_FACTOR_REGISTRATION), + argThat(isEqualTo(TwoFactorRegisterParams.of(player)))); verifyZeroInteractions(emailService); } @@ -208,8 +217,6 @@ public class RegisterCommandTest { given(commonService.getProperty(RegistrationSettings.REGISTER_SECOND_ARGUMENT)).willReturn(RegisterSecondaryArgument.CONFIRMATION); given(emailService.hasAllInformation()).willReturn(true); Player player = mock(Player.class); - RegistrationExecutor executor = mock(RegistrationExecutor.class); - given(registrationExecutorProvider.getEmailRegisterExecutor(player, playerMail)).willReturn(executor); // when command.executeCommand(player, Arrays.asList(playerMail, playerMail)); @@ -217,7 +224,8 @@ public class RegisterCommandTest { // then verify(validationService).validateEmail(playerMail); verify(emailService).hasAllInformation(); - verify(management).performRegister(player, executor); + verify(management).performRegister(eq(RegistrationMethod.EMAIL_REGISTRATION), + argThat(isEqualTo(EmailRegisterParams.of(player, playerMail)))); } @Test @@ -239,14 +247,13 @@ public class RegisterCommandTest { public void shouldPerformPasswordRegistration() { // given Player player = mock(Player.class); - RegistrationExecutor executor = mock(RegistrationExecutor.class); - given(registrationExecutorProvider.getPasswordRegisterExecutor(player, "myPass", null)).willReturn(executor); // when command.executeCommand(player, Collections.singletonList("myPass")); // then - verify(management).performRegister(player, executor); + verify(management).performRegister(eq(RegistrationMethod.PASSWORD_REGISTRATION), + argThat(isEqualTo(PasswordRegisterParams.of(player, "myPass", null)))); } @Test @@ -257,15 +264,14 @@ public class RegisterCommandTest { String email = "email@example.org"; given(validationService.validateEmail(email)).willReturn(true); Player player = mock(Player.class); - RegistrationExecutor executor = mock(RegistrationExecutor.class); - given(registrationExecutorProvider.getPasswordRegisterExecutor(player, "myPass", email)).willReturn(executor); // when command.executeCommand(player, Arrays.asList("myPass", email)); // then verify(validationService).validateEmail(email); - verify(management).performRegister(player, executor); + verify(management).performRegister(eq(RegistrationMethod.PASSWORD_REGISTRATION), + argThat(isEqualTo(PasswordRegisterParams.of(player, "myPass", email)))); } @Test @@ -292,14 +298,64 @@ public class RegisterCommandTest { given(commonService.getProperty(RegistrationSettings.REGISTRATION_TYPE)).willReturn(RegistrationType.PASSWORD); given(commonService.getProperty(RegistrationSettings.REGISTER_SECOND_ARGUMENT)).willReturn(RegisterSecondaryArgument.EMAIL_OPTIONAL); Player player = mock(Player.class); - RegistrationExecutor executor = mock(RegistrationExecutor.class); - given(registrationExecutorProvider.getPasswordRegisterExecutor(eq(player), anyString(), eq(null))).willReturn(executor); // when command.executeCommand(player, Collections.singletonList("myPass")); // then - verify(registrationExecutorProvider).getPasswordRegisterExecutor(player, "myPass", null); - verify(management).performRegister(player, executor); + verify(management).performRegister(eq(RegistrationMethod.PASSWORD_REGISTRATION), + argThat(isEqualTo(PasswordRegisterParams.of(player, "myPass", null)))); + } + + + // TODO ljacqu 20170317: Document and extract as util + + private static

Matcher

isEqualTo(P expected) { + return new TypeSafeMatcher

() { + @Override + protected boolean matchesSafely(RegistrationParameters item) { + assertAreParamsEqual(expected, item); + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("parameters " + expected); + } + }; + } + + private static void assertAreParamsEqual(RegistrationParameters lhs, RegistrationParameters rhs) { + if (lhs.getClass() != rhs.getClass()) { + fail("Params classes don't match, got " + lhs.getClass().getSimpleName() + + " and " + rhs.getClass().getSimpleName()); + } + + List fieldsToCheck = getFields(lhs); + for (Field field : fieldsToCheck) { + Object lhsValue = ReflectionTestUtils.getFieldValue(field, lhs); + Object rhsValue = ReflectionTestUtils.getFieldValue(field, rhs); + if (!Objects.equals(lhsValue, rhsValue)) { + fail("Field '" + field.getName() + "' does not have same value: '" + + lhsValue + "' vs. '" + rhsValue + "'"); + } + } + } + + private static List getFields(RegistrationParameters params) { + List fields = new ArrayList<>(); + Class currentClass = params.getClass(); + while (currentClass != null) { + for (Field f : currentClass.getDeclaredFields()) { + if (!Modifier.isStatic(f.getModifiers())) { + fields.add(f); + } + } + if (currentClass == RegistrationParameters.class) { + break; + } + currentClass = currentClass.getSuperclass(); + } + return fields; } } diff --git a/src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java b/src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java index b845b8d5..dee3dc64 100644 --- a/src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java +++ b/src/test/java/fr/xephi/authme/process/register/AsyncRegisterTest.java @@ -3,9 +3,13 @@ package fr.xephi.authme.process.register; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.initialization.factory.SingletonStore; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.process.register.executors.PasswordRegisterParams; import fr.xephi.authme.process.register.executors.RegistrationExecutor; +import fr.xephi.authme.process.register.executors.RegistrationMethod; +import fr.xephi.authme.process.register.executors.TwoFactorRegisterParams; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RestrictionSettings; @@ -16,6 +20,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.only; @@ -39,6 +44,8 @@ public class AsyncRegisterTest { private CommonService commonService; @Mock private DataSource dataSource; + @Mock + private SingletonStore registrationExecutorStore; @Test public void shouldDetectAlreadyLoggedInPlayer() { @@ -47,9 +54,10 @@ public class AsyncRegisterTest { Player player = mockPlayerWithName(name); given(playerCache.isAuthenticated(name)).willReturn(true); RegistrationExecutor executor = mock(RegistrationExecutor.class); + singletonStoreWillReturn(registrationExecutorStore, executor); // when - asyncRegister.register(player, executor); + asyncRegister.register(RegistrationMethod.PASSWORD_REGISTRATION, PasswordRegisterParams.of(player, "abc", null)); // then verify(commonService).send(player, MessageKey.ALREADY_LOGGED_IN_ERROR); @@ -64,9 +72,10 @@ public class AsyncRegisterTest { given(playerCache.isAuthenticated(name)).willReturn(false); given(commonService.getProperty(RegistrationSettings.IS_ENABLED)).willReturn(false); RegistrationExecutor executor = mock(RegistrationExecutor.class); + singletonStoreWillReturn(registrationExecutorStore, executor); // when - asyncRegister.register(player, executor); + asyncRegister.register(RegistrationMethod.TWO_FACTOR_REGISTRATION, TwoFactorRegisterParams.of(player)); // then verify(commonService).send(player, MessageKey.REGISTRATION_DISABLED); @@ -82,9 +91,10 @@ public class AsyncRegisterTest { given(commonService.getProperty(RegistrationSettings.IS_ENABLED)).willReturn(true); given(dataSource.isAuthAvailable(name)).willReturn(true); RegistrationExecutor executor = mock(RegistrationExecutor.class); + singletonStoreWillReturn(registrationExecutorStore, executor); // when - asyncRegister.register(player, executor); + asyncRegister.register(RegistrationMethod.TWO_FACTOR_REGISTRATION, TwoFactorRegisterParams.of(player)); // then verify(commonService).send(player, MessageKey.NAME_ALREADY_REGISTERED); @@ -93,6 +103,7 @@ public class AsyncRegisterTest { } @Test + @SuppressWarnings("unchecked") public void shouldStopForFailedExecutorCheck() { // given String name = "edbert"; @@ -103,14 +114,16 @@ public class AsyncRegisterTest { given(commonService.getProperty(RestrictionSettings.MAX_REGISTRATION_PER_IP)).willReturn(0); given(dataSource.isAuthAvailable(name)).willReturn(false); RegistrationExecutor executor = mock(RegistrationExecutor.class); - given(executor.isRegistrationAdmitted()).willReturn(false); + TwoFactorRegisterParams params = TwoFactorRegisterParams.of(player); + given(executor.isRegistrationAdmitted(params)).willReturn(false); + singletonStoreWillReturn(registrationExecutorStore, executor); // when - asyncRegister.register(player, executor); + asyncRegister.register(RegistrationMethod.TWO_FACTOR_REGISTRATION, params); // then verify(dataSource, only()).isAuthAvailable(name); - verify(executor, only()).isRegistrationAdmitted(); + verify(executor, only()).isRegistrationAdmitted(params); } private static Player mockPlayerWithName(String name) { @@ -118,4 +131,10 @@ public class AsyncRegisterTest { given(player.getName()).willReturn(name); return player; } + + @SuppressWarnings("unchecked") + private static void singletonStoreWillReturn(SingletonStore store, + RegistrationExecutor mock) { + given(store.getSingleton(any(Class.class))).willReturn(mock); + } } diff --git a/src/test/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutorProviderTest.java b/src/test/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutorProviderTest.java index 9ca9c7aa..6aa22161 100644 --- a/src/test/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutorProviderTest.java +++ b/src/test/java/fr/xephi/authme/process/register/executors/EmailRegisterExecutorProviderTest.java @@ -1,6 +1,5 @@ package fr.xephi.authme.process.register.executors; -import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.TestHelper; import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; @@ -34,13 +33,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; /** - * Test for {@link EmailRegisterExecutorProvider}. + * Test for {@link EmailRegisterExecutor}. */ @RunWith(MockitoJUnitRunner.class) public class EmailRegisterExecutorProviderTest { @InjectMocks - private EmailRegisterExecutorProvider emailRegisterExecutorProvider; + private EmailRegisterExecutor executor; @Mock private PermissionsManager permissionsManager; @@ -62,10 +61,10 @@ public class EmailRegisterExecutorProviderTest { String email = "test@example.com"; given(dataSource.countAuthsByEmail(email)).willReturn(4); Player player = mock(Player.class); - RegistrationExecutor executor = emailRegisterExecutorProvider.new EmailRegisterExecutor(player, email); + EmailRegisterParams params = EmailRegisterParams.of(player, email); // when - boolean result = executor.isRegistrationAdmitted(); + boolean result = executor.isRegistrationAdmitted(params); // then assertThat(result, equalTo(false)); @@ -80,10 +79,10 @@ public class EmailRegisterExecutorProviderTest { given(commonService.getProperty(EmailSettings.MAX_REG_PER_EMAIL)).willReturn(3); Player player = mock(Player.class); given(permissionsManager.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)).willReturn(true); - RegistrationExecutor executor = emailRegisterExecutorProvider.new EmailRegisterExecutor(player, "test@example.com"); + EmailRegisterParams params = EmailRegisterParams.of(player, "test@example.com"); // when - boolean result = executor.isRegistrationAdmitted(); + boolean result = executor.isRegistrationAdmitted(params); // then assertThat(result, equalTo(true)); @@ -97,10 +96,10 @@ public class EmailRegisterExecutorProviderTest { String email = "test@example.com"; given(dataSource.countAuthsByEmail(email)).willReturn(0); Player player = mock(Player.class); - RegistrationExecutor executor = emailRegisterExecutorProvider.new EmailRegisterExecutor(player, "test@example.com"); + EmailRegisterParams params = EmailRegisterParams.of(player, "test@example.com"); // when - boolean result = executor.isRegistrationAdmitted(); + boolean result = executor.isRegistrationAdmitted(params); // then assertThat(result, equalTo(true)); @@ -120,10 +119,10 @@ public class EmailRegisterExecutorProviderTest { World world = mock(World.class); given(world.getName()).willReturn("someWorld"); given(player.getLocation()).willReturn(new Location(world, 48, 96, 144)); - RegistrationExecutor executor = emailRegisterExecutorProvider.new EmailRegisterExecutor(player, "test@example.com"); + EmailRegisterParams params = EmailRegisterParams.of(player, "test@example.com"); // when - PlayerAuth auth = executor.buildPlayerAuth(); + PlayerAuth auth = executor.buildPlayerAuth(params); // then assertThat(auth, hasAuthBasicData("veronica", "Veronica", "test@example.com", "123.45.67.89")); @@ -132,18 +131,17 @@ public class EmailRegisterExecutorProviderTest { } @Test - @SuppressWarnings("unchecked") public void shouldPerformActionAfterDataSourceSave() { // given given(emailService.sendPasswordMail(anyString(), anyString(), anyString())).willReturn(true); Player player = mock(Player.class); given(player.getName()).willReturn("Laleh"); - RegistrationExecutor executor = emailRegisterExecutorProvider.new EmailRegisterExecutor(player, "test@example.com"); + EmailRegisterParams params = EmailRegisterParams.of(player, "test@example.com"); String password = "A892C#@"; - ReflectionTestUtils.setField((Class) executor.getClass(), executor, "password", password); + params.setPassword(password); // when - executor.executePostPersistAction(); + executor.executePostPersistAction(params); // then verify(emailService).sendPasswordMail("Laleh", "test@example.com", password); @@ -151,18 +149,17 @@ public class EmailRegisterExecutorProviderTest { } @Test - @SuppressWarnings("unchecked") public void shouldHandleEmailSendingFailure() { // given given(emailService.sendPasswordMail(anyString(), anyString(), anyString())).willReturn(false); Player player = mock(Player.class); given(player.getName()).willReturn("Laleh"); - RegistrationExecutor executor = emailRegisterExecutorProvider.new EmailRegisterExecutor(player, "test@example.com"); + EmailRegisterParams params = EmailRegisterParams.of(player, "test@example.com"); String password = "A892C#@"; - ReflectionTestUtils.setField((Class) executor.getClass(), executor, "password", password); + params.setPassword(password); // when - executor.executePostPersistAction(); + executor.executePostPersistAction(params); // then verify(emailService).sendPasswordMail("Laleh", "test@example.com", password); diff --git a/src/test/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutorProviderTest.java b/src/test/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutorTest.java similarity index 81% rename from src/test/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutorProviderTest.java rename to src/test/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutorTest.java index a033975f..6c103944 100644 --- a/src/test/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutorProviderTest.java +++ b/src/test/java/fr/xephi/authme/process/register/executors/PasswordRegisterExecutorTest.java @@ -34,13 +34,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; /** - * Test for {@link PasswordRegisterExecutorProvider}. + * Test for {@link PasswordRegisterExecutor}. */ @RunWith(MockitoJUnitRunner.class) -public class PasswordRegisterExecutorProviderTest { +public class PasswordRegisterExecutorTest { @InjectMocks - private PasswordRegisterExecutorProvider passwordRegisterExecutorProvider; + private PasswordRegisterExecutor executor; @Mock private ValidationService validationService; @@ -62,10 +62,10 @@ public class PasswordRegisterExecutorProviderTest { String name = "player040"; given(validationService.validatePassword(password, name)).willReturn(new ValidationResult()); Player player = mockPlayerWithName(name); - RegistrationExecutor executor = passwordRegisterExecutorProvider.new PasswordRegisterExecutor(player, password, null); + PasswordRegisterParams params = PasswordRegisterParams.of(player, password, null); // when - boolean result = executor.isRegistrationAdmitted(); + boolean result = executor.isRegistrationAdmitted(params); // then assertThat(result, equalTo(true)); @@ -80,10 +80,10 @@ public class PasswordRegisterExecutorProviderTest { given(validationService.validatePassword(password, name)).willReturn( new ValidationResult(MessageKey.PASSWORD_CHARACTERS_ERROR, "[a-z]")); Player player = mockPlayerWithName(name); - RegistrationExecutor executor = passwordRegisterExecutorProvider.new PasswordRegisterExecutor(player, password, null); + PasswordRegisterParams params = PasswordRegisterParams.of(player, password, null); // when - boolean result = executor.isRegistrationAdmitted(); + boolean result = executor.isRegistrationAdmitted(params); // then assertThat(result, equalTo(false)); @@ -101,10 +101,10 @@ public class PasswordRegisterExecutorProviderTest { World world = mock(World.class); given(world.getName()).willReturn("someWorld"); given(player.getLocation()).willReturn(new Location(world, 48, 96, 144)); - RegistrationExecutor executor = passwordRegisterExecutorProvider.new PasswordRegisterExecutor(player, "pass", "mail@example.org"); + PasswordRegisterParams params = PasswordRegisterParams.of(player, "pass", "mail@example.org"); // when - PlayerAuth auth = executor.buildPlayerAuth(); + PlayerAuth auth = executor.buildPlayerAuth(params); // then assertThat(auth, hasAuthBasicData("s1m0n", "S1m0N", "mail@example.org", "123.45.67.89")); @@ -118,10 +118,10 @@ public class PasswordRegisterExecutorProviderTest { given(commonService.getProperty(RegistrationSettings.FORCE_LOGIN_AFTER_REGISTER)).willReturn(false); given(commonService.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(false); Player player = mock(Player.class); - RegistrationExecutor executor = passwordRegisterExecutorProvider.new PasswordRegisterExecutor(player, "pass", "mail@example.org"); + PasswordRegisterParams params = PasswordRegisterParams.of(player, "pass", "mail@example.org"); // when - executor.executePostPersistAction(); + executor.executePostPersistAction(params); // then TestHelper.runSyncDelayedTaskWithDelay(bukkitService); @@ -134,10 +134,10 @@ public class PasswordRegisterExecutorProviderTest { // given given(commonService.getProperty(RegistrationSettings.FORCE_LOGIN_AFTER_REGISTER)).willReturn(true); Player player = mock(Player.class); - RegistrationExecutor executor = passwordRegisterExecutorProvider.new PasswordRegisterExecutor(player, "pass", "mail@example.org"); + PasswordRegisterParams params = PasswordRegisterParams.of(player, "pass", "mail@example.org"); // when - executor.executePostPersistAction(); + executor.executePostPersistAction(params); // then verifyZeroInteractions(bukkitService, asynchronousLogin);